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

Сериализация объектов платформы Entity с отношением "один к большому"

Я пытаюсь использовать EF с Code First и Web API. У меня нет никаких проблем, пока я не перейду к сериализации отношений "многие-ко-многим". Когда я пытаюсь выполнить следующий метод web api ниже, я получаю следующее сообщение об ошибке:

public class TagsController : ApiController
{

        private BlogDataContext db = new BlogDataContext();

        // GET api/Tags
        public IEnumerable<Tag> GetTags()
        {
            return db.Tags.AsEnumerable();
        }
}

Я получаю следующую ошибку:

'System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D' с именем контракта с данными 'Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D: http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' не ожидается. Рассмотрите возможность использования DataContractResolver или добавьте типы, неизвестные статически списку известных типов - например, используя атрибут KnownTypeAttribute или добавив их в список известных типов, переданных в DataContractSerializer.

Я прочитал некоторые статьи SO (статья 1, статья 2), что исправление заключается в добавлении следующий атрибут:

[DataContract (IsReference = true)]

но это не повлияло. Также использование [IgnoreDataMember] не имеет эффекта. Единственный вариант, который, похоже, работает, - установить для параметра Configuration.ProxyCreationEnabled значение false. Это мой единственный вариант? Я что-то пропустил?

Примеры объектов POCO:

Тег

[DataContract(IsReference = true)]
public class Tag
{
        public Tag()
        {
            this.Blogs = new HashSet<Blog>();
        }

        [Key]
        [DataMember]
        public int Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        [IgnoreDataMember]
        public virtual ICollection<Blog> Blogs { get; set; }
}

Блог

[DataContract(IsReference = true)]
public class Blog
{
    public Blog()
    {
        this.Tags = new HashSet<Tag>();
    }

    [Key]
    [DataMember]
    public int Id { get; set; }

    [DataMember]
    public string Name { get; set; }

    [IgnoreDataMember]
    public virtual ICollection<Tag> Tags { get; set; }
}
4b9b3361

Ответ 1

Когда вы видите такой объект, как:

System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D

Это исполняемая версия EF Generated версии прокси для того, что обычно считается объектом POCO.

Entity Framework создал этот объект, потому что он отслеживает, когда объекты изменились, поэтому, когда вы вызываете .SaveChanges(), он может оптимизировать, что делать. Падение этого состоит в том, что вы фактически не используете определенный объект, который вы определили, поэтому Data Contracts and Frameworks (Json.net) не могут использовать их, поскольку они будут иметь ваш исходный объект POCO.

Чтобы предотвратить возврат EF из этого объекта, у вас есть два варианта (ATM):

Сначала Попробуйте отключить создание прокси-объекта в вашем DbContext.

DbContext.Configuration.ProxyCreationEnabled = false;

Это полностью отключит создание объектов Proxy для каждого запроса в конкретном DbContext. (Это не влияет на кешированный объект в ObjectContext).

Во-вторых, используйте EntityFramework 5.0 + с AsNoTracking() (ProxyCreationEnabled по-прежнему доступен в EF 5.0)

Вы также должны иметь возможность

DbContext.Persons.AsNoTracking().FirstOrDefault();

или

DbContext.Persons.
  .Include(i => i.Parents)
  .AsNoTracking()
  .FirstOrDefault();

Вместо того, чтобы глобально отключать создание прокси для DbContext, это только отключает его для каждого запроса. (Это влияет на кешированный объект в ObjectContext, он не кэшируется)

Ответ 2

Я хотел оставить бездействие прокси-сервера и обнаружил, что использование ProxyDataContractResolver, похоже, решило проблему для меня. См. msdn для справки о том, как использовать его в wcf, что не является точно WebAPI, но должно заставить вас идти по правильному пути.

Ответ 3

db.Configuration.ProxyCreationEnabled = false;

решит вашу проблему