I am attempting to retrieve a list of objects from Entity Framework via WCF, but am receiving the following exception:
There was an error while trying to serialize parameter http://tempuri.org/:GetAllResult. The InnerException message was 'Type 'System.Data.Entity.DynamicProxies.TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE' with data contract name 'TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Pleas开发者_开发知识库e see InnerException for more details.
I have used WCF in the past, but never with Entity Framework. I have all my entities generated via Entity Framework and are annotated with [DataContract] and [DataMember] attributes. I have no Navigation Properties on any of my entities.
The GetAll() method being called is in an abstract service class:
[ServiceContract]
public interface IService<T>
{
[OperationContract]
List<T> GetAll();
}
And I am using the ChannelFactory to call my implementation:
Binding binding = new NetTcpBinding();
EndpointAddress endpointAddress = new EndpointAddress("net.tcp://localhost:8081/" + typeof(TestObjectService).Name);
using (ChannelFactory<ITestObjectService> channel = new ChannelFactory<ITestObjectService>(binding, endpointAddress))
{
ITestObjectService testObjectService = channel.CreateChannel();
testObjects = testObjectService.GetAll();
channel.Close();
}
I am hosting it as such:
Type type = typeof(TestObjectService);
ServiceHost host = new ServiceHost(type,
new Uri("http://localhost:8080/" + type.Name),
new Uri("net.tcp://localhost:8081/" + type.Name));
host.Open();
When using debugging, it finds the objects from the database, however, it is failing returning the objects.
Any ideas as to where I may be going wrong?
This was a pain to figure out but it is because EntityFramework creates a 'proxy' of your class. The TestObject class I had was setup correctly, but it was creating a class called: TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE
To make the ChannelFactory + WCF + Entity Framework all work together, you must go into your Context constructor and add the following:
ContextOptions.ProxyCreationEnabled = false;
When using the DbContext API for Code First (EF 4.3) I had to do:
public class MyClass : DbContext
{
public MyClass()
{
base.Configuration.ProxyCreationEnabled = false;
}
}
For EntityFramework 6.0 I had to change configuration as well:
public class MyContext : DbContext
{
public MyContext() : base("name=MyContext")
{
Configuration.ProxyCreationEnabled = false;
}
}
You have several other options other than adding no proxy to your entire POCO:
1) Create a wrapper/DTO. In an API, it is likely that you don't want to expose the whole POCO to your users... so create a wrapper object that only exposes the stuff you want, and this also solves the proxy problem.
1.5) Pretty much like 1, but instead of creating a wrapper, just return an anonymous type
(with LINQ
)
2) If you don't need to do it app wide, it may make more sense to do it in the Controller
where you need that serialization... or even more localized to a Method
, including using
, here's a per Controller
implementation:
public class ThingController : ApiController
{
public ThingController()
{
db = new MyContext();
db.Configuration.ProxyCreationEnabled = false;
}
private MyContext db;
// GET api/Thing
public IQueryable<Thing> GetThings()
{
return db.Things;
}
//...
protected override void Dispose(bool disposing)
{
if (disposing)
db.Dispose();
base.Dispose(disposing);
}
}
3) The other thing is if you're needing it just for that db call, the easiest way to do it is to chain AsNoTracking()
into your call:
List<Thing> things;
using (var db = new MyContext())
{
things = db.Things.AsNoTracking().ToList();
}
You can instead use a DTO and return that. No need to turn off the Proxycreationenabled property.
精彩评论