Сериализация объектов EF4.1 с использованием JSON.Net - PullRequest
6 голосов
/ 09 августа 2011

Я создаю приложение, используя MVC3, Razor View Engine, Шаблон репозитория с единицей работы и использую код EF4.1 First для определения моей модели данных.

Вот немного фона (если хотите, закрасьте его).

Само приложение представляет собой «Меню» Интранета.

2 основных объекта - это MenuItem и Департамент, из которых:

  • MenuItem может иметь много отделов
  • У отделов может быть много MenuItems
  • MenuItem может иметь MenuItem в качестве родителя

Так я определил свои сущности

public class MenuItem
{
   public int MenuItemId { get; set; }
   public string Name { get; set; }
   public string Url { get; set; }
   public virtual ICollection<Department> Departments { get; set; }
   public int? ParentId { get; set; }
   public virtual MenuItem ParentMenuItem { get; set; }
}

public class Department
{
   public int DepartmentId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<MenuItem> MenuItems { get; set; }
}

Я использую FluentAPI для определения собственной ссылки «многие ко многим» для MenuItem.

У меня проблема с передачей MenuItem в представление через JSON. Основные проблемы заключаются в том, что у меня есть круговая ссылка между объектами, с которой встроенный анализатор JSON не может справиться, и у меня все еще включена отложенная загрузка и генерация прокси.

Я использую библиотеку JSON.net от Nuget в качестве моего сериализатора JSON, так как это кажется хорошим способом решения проблемы циклических ссылок. Теперь я не уверен, как «исправить» проблему генерации прокси. В настоящее время сериализатор выбрасывает The RelationshipManager object could not be serialized. This type of object cannot be serialized when the RelationshipManager belongs to an entity object that does not implement IEntityWithRelationships.

Может кто-нибудь помочь мне с этим? Если я отключу генерацию прокси, у меня будет адская загрузка всех детей MenuItem, поэтому я хочу оставить это включенным. Я прочитал достаточно много, и, кажется, существует множество разных ответов, включая проецирование сущностей в другой объект и сериализацию этого и т. Д., И т. Д. В идеале был бы какой-то способ настройки JSON.net для игнорирования объекта RelationshipManager?

Обновление

Вот что я использовал как пользовательский ContractResolver для сериализатора JSON.Net. Кажется, это решило мою проблему.

public class ContractResolver : DefaultContractResolver
{
    private static readonly IEnumerable<Type> Types = GetEntityTypes();
    private static IEnumerable<Type> GetEntityTypes()
    {
        var assembly = Assembly.GetAssembly(typeof (IEntity));
        var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, "Namespace", StringComparison.Ordinal));
        return types;
    }

    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        if (!AllowType(objectType))
            return new List<MemberInfo>();

        var members = base.GetSerializableMembers(objectType);
        members.RemoveAll(memberInfo => (IsMemberEntityWrapper(memberInfo)));
        return members;
    }

    private static bool AllowType(Type objectType)
    {
        return Types.Contains(objectType) || Types.Contains(objectType.BaseType);
    }

    private static bool IsMemberEntityWrapper(MemberInfo memberInfo)
    {
        return memberInfo.Name == "_entityWrapper";
    }
}

IEntity - это интерфейс, реализуемый всеми объектами сущности Code First.

Ответы [ 2 ]

2 голосов
/ 10 марта 2012

Я понимаю, что на этот вопрос есть приемлемый ответ, но я подумал, что опубликую свое решение EF Code First для будущих зрителей.Мне удалось обойти сообщение об ошибке с помощью распознавателя контракта ниже:

 class ContractResolver : DefaultContractResolver
 {
      protected override List<System.Reflection.MemberInfo> GetSerializableMembers(Type objectType)
      {
           if (objectType.Namespace.StartsWith("System.Data.Entity.Dynamic"))
           {
                return base.GetSerializableMembers(objectType.BaseType);
           }

           return base.GetSerializableMembers(objectType);
      }
 }

Это работает, потому что классы EF Code First наследуют класс POCO, который вы на самом деле хотите сериализовать, поэтому, если мы сможем определить, когда мыГлядя на сгенерированный EF класс (проверяя пространство имен), мы можем просто сериализовать, используя свойства из базового класса, и, следовательно, только сериализовать свойства POCO, которые мы действительно использовали в первую очередь.

0 голосов
/ 09 августа 2011

Ну, вы использовали мощный API-интерфейс сериализации, который также сериализует ссылки и все элементы, и теперь вы жалуетесь, что он сериализует все элементы :)

Я не проверял это, но я верю, что это приблизит вас к решению.

JSON.NET - довольно мощный инструмент, и он должен предложить вам точку расширяемости, чтобы избежать такого поведения, но вам придется кодировать его самостоятельно. Вам понадобится пользовательский DataContractResolver, где вы определяете, какие элементы должны быть сериализованы. Здесь - аналогичный пример для NHibernate.

Вы можете реализовать некоторую логику, которая будет принимать только члены, присутствующие в родительском классе динамического прокси. Надеюсь это не сломит ленивую загрузку. Чтобы проверить, что текущий объект является прокси, вы можете использовать этот код для получения всех известных типов прокси:

IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();
...