开发者

DataContractSerializer Error using Entity Framework 4.0 with WCF 4.0

开发者 https://www.devze.com 2023-01-10 02:16 出处:网络
I am attempting to retrieve a list of objects from Entity Framework via WCF, but am receiving the following exception:

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号