Ошибка 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();
При использовании отладки он находит объекты из базы данных, однако он не возвращает объекты.
Любые идеи относительно того, где я могу ошибиться?
Ответы
Ответ 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 type
(с LINQ
)
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.