Подтвердить что ты не робот

Ошибка DataContractSerializer при использовании Entity Framework 4.0 с WCF 4.0

Я пытаюсь получить список объектов из Entity Framework через WCF, но получаю следующее исключение:

При попытке сериализовать параметр http://tempuri.org/:GetAllResult произошла ошибка. Сообщение InnerException был "Type 'System.Data.Entity.DynamicProxies.TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE' с именем контракта данных 'TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE: http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' не ожидается, Подумайте об использовании DataContractResolver или добавьте любые типы, не известные статически в список известных типов - например, с помощью атрибута KnownTypeAttribute или путем добавления их в список известных типов, переданных DataContractSerializer. '. Дополнительную информацию см. В InnerException.

Я использовал WCF в прошлом, но никогда не использовал Entity Framework. У меня есть все мои сущности, созданные через Entity Framework, и аннотируются с атрибутами [DataContract] и [DataMember]. У меня нет свойств навигации для любых моих объектов.

Вызывается метод GetAll() в абстрактном классе сервиса:

[ServiceContract]
public interface IService<T>
{
    [OperationContract]
    List<T> GetAll();
}

И я использую ChannelFactory для вызова моей реализации:

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();
}

Я размещаю его как таковой:

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();

При использовании отладки он находит объекты из базы данных, однако он не возвращает объекты.

Любые идеи относительно того, где я могу ошибиться?

4b9b3361

Ответ 1

Это была боль, чтобы понять, но это потому, что EntityFramework создает "прокси" вашего класса. Класс TestObject, который у меня был, был настроен правильно, но он создавал класс, называемый: TestObject_240F2B681A782799F3A0C3AFBE4A67A7E86083C3CC4A3939573C5410B408ECCE

Чтобы сделать ChannelFactory + WCF + Entity Framework все вместе, вы должны войти в свой конструктор контекста и добавить следующее:

ContextOptions.ProxyCreationEnabled = false;

Я надеюсь, что это поможет кому-то другому.

Ответ 2

При использовании API DbContext для Code First (EF 4.3) мне пришлось делать:

public class MyClass : DbContext
{
    public MyClass()
    {
        base.Configuration.ProxyCreationEnabled = false;
    }
}

Ответ 3

Для EntityFramework 6.0 мне также пришлось изменить конфигурацию:

public class MyContext : DbContext
{
    public MyContext() : base("name=MyContext")
    {
        Configuration.ProxyCreationEnabled = false;
    }
}

Ответ 4

У вас есть несколько других параметров, кроме добавления прокси-сервера для всего POCO:

1) Создайте обертку. В API, скорее всего, вы не хотите подвергать весь POCO своим пользователям... поэтому создайте объект-оболочку, который предоставляет только то, что вам нужно, и это также решает проблему прокси.

1.5) Довольно похоже на 1, но вместо создания обертки просто верните anonymous typeLINQ)

2) Если вам не нужно делать это достаточно широко, это может иметь смысл сделать это в Controller, где вам нужна эта сериализация... или даже более локализованная в Method, включая using, здесь реализация за Controller:

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) Другое дело, если вам нужно это только для этого вызова db, самый простой способ сделать это - связать AsNoTracking() с вашим вызовом:

List<Thing> things;
using (var db = new MyContext())
{
    things = db.Things.AsNoTracking().ToList();
}

Ответ 5

Вместо этого вы можете использовать DTO и вернуть это. Не нужно отключать свойство Proxycreationenabled.