JSON.NET и nHibernate Ленивая загрузка коллекций - PullRequest
31 голосов
/ 13 ноября 2008

Кто-нибудь использует JSON.NET с nHibernate? Я замечаю, что получаю ошибки при попытке загрузить класс с дочерними коллекциями.

Ответы [ 6 ]

42 голосов
/ 08 мая 2011

Я столкнулся с той же проблемой, поэтому попытался использовать код @ Liedman, но GetSerializableMembers() никогда не вызывали для прокси-ссылки. Я нашел другой метод для переопределения:

  public class NHibernateContractResolver : DefaultContractResolver
  {
      protected override JsonContract CreateContract(Type objectType)
      {
          if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
              return base.CreateContract(objectType.BaseType);
          else
              return base.CreateContract(objectType);
      }
  }
24 голосов
/ 17 марта 2010

У нас была именно эта проблема, которая была решена вдохновением от ответа Ремесленника здесь.

Проблема возникает из-за того, что JSON.NET запутался в том, как сериализовать прокси-классы NHibernate. Решение: сериализуйте экземпляры прокси как их базовый класс.

Упрощенная версия кода Handcraftsman выглядит следующим образом:

public class NHibernateContractResolver : DefaultContractResolver {
    protected override List<MemberInfo> GetSerializableMembers(Type objectType) {
        if (typeof(INHibernateProxy).IsAssignableFrom(objectType)) {
            return base.GetSerializableMembers(objectType.BaseType);
        } else {
            return base.GetSerializableMembers(objectType);
        }
    }
}

ИМХО, этот код имеет то преимущество, что все еще полагается на поведение JSON.NET по умолчанию в отношении пользовательских атрибутов и т. Д. (А код намного короче!).

Используется вот так

        var serializer = new JsonSerializer{
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            ContractResolver = new NHibernateContractResolver()
        };
        StringWriter stringWriter = new StringWriter();
        JsonWriter jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);                
        serializer.Serialize(jsonWriter, objectToSerialize);
        string serializedObject = stringWriter.ToString();

Примечание: Этот код был написан для NHibernate 2.1 и использовался для него. Как отмечают некоторые комментаторы, в более поздних версиях NHibernate он не работает «из коробки», вам придется внести некоторые изменения. Я постараюсь обновить код, если мне когда-нибудь придется делать это с более поздними версиями NHibernate.

18 голосов
/ 30 октября 2009

Я использую NHibernate с Json.NET и заметил, что я получаю необъяснимые свойства "__interceptors" в моих сериализованных объектах. Поиск в Google показал это превосходное решение от Ли Хенсона, которое я адаптировал для работы с Json.NET 3.5 Release 5 следующим образом.

public class NHibernateContractResolver : DefaultContractResolver
{
  private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers();

  protected override List<MemberInfo> GetSerializableMembers(Type objectType)
  {
    var members = base.GetSerializableMembers(objectType);

    members.RemoveAll(memberInfo =>
                      (IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
                      (IsMemberDynamicProxyMixin(memberInfo)) ||
                      (IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
                      (IsMemberInheritedFromProxySuperclass(memberInfo, objectType)));

    var actualMemberInfos = new List<MemberInfo>();

    foreach (var memberInfo in members)
    {
      var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
      actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]);
    }

    return actualMemberInfos;
  }

  private static bool IsMemberDynamicProxyMixin(MemberInfo memberInfo)
  {
    return memberInfo.Name == "__interceptors";
  }

  private static bool IsMemberInheritedFromProxySuperclass(MemberInfo memberInfo, Type objectType)
  {
    return memberInfo.DeclaringType.Assembly == typeof(INHibernateProxy).Assembly;
  }

  private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType)
  {
    var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType)
                  ? objectType.BaseType.GetMember(memberInfo.Name)
                  : objectType.GetMember(memberInfo.Name);

    return infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0;
  }

  private static bool IsMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo)
  {
    return Array.Exists(NHibernateProxyInterfaceMembers, mi => memberInfo.Name == mi.Name);
  }
}

Чтобы использовать его, просто поместите экземпляр в свойство ContractResolver вашего JsonSerializer. Проблема циклической зависимости, отмеченная jishi, может быть решена путем установки свойства ReferenceLoopHandling в ReferenceLoopHandling.Ignore. Вот метод расширения, который можно использовать для сериализации объектов с использованием Json.Net

  public static void SerializeToJsonFile<T>(this T itemToSerialize, string filePath)
  {
    using (StreamWriter streamWriter = new StreamWriter(filePath))
    {
      using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
      {
        jsonWriter.Formatting = Formatting.Indented;
        JsonSerializer serializer = new JsonSerializer
          {
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            ContractResolver = new NHibernateContractResolver(),
          };
        serializer.Serialize(jsonWriter, itemToSerialize);
      }
    }
  }
3 голосов
/ 17 ноября 2008

Возможно, вы захотите загрузить большую часть объекта, чтобы его можно было сериализовать:

        ICriteria ic = _session.CreateCriteria(typeof(Person));

        ic.Add(Restrictions.Eq("Id", id));

        if (fetchEager)
        {
            ic.SetFetchMode("Person", FetchMode.Eager);
        }

Хороший способ сделать это - добавить bool в конструктор (bool isFetchEager) вашего метода провайдера данных.

3 голосов
/ 13 ноября 2008

Получаете ли вы круговую ошибку зависимости? Как вы игнорируете объекты из сериализации?

Поскольку отложенная загрузка создает прокси-объекты, все атрибуты, которые есть у ваших учеников, будут потеряны. Я столкнулся с той же проблемой с JSON-сериализатором Newtonsoft, поскольку прокси-объект больше не имел атрибутов [JsonIgnore].

1 голос
/ 07 апреля 2015

Я бы сказал, что это проблема дизайна на мой взгляд. Поскольку NH устанавливает соединения с базой данных под всеми и имеет посредники в середине, для прозрачности вашего приложения нецелесообразно напрямую сериализовать их (и, как вы можете видеть, Json.NET их совсем не любит).

Вы не должны сериализовать сами сущности, но вы должны преобразовать их в объекты «просмотра» или объекты POCO или DTO (как вы хотите их называть), а затем сериализовать их.

Разница в том, что, хотя сущность NH может иметь прокси, ленивые атрибуты и т. Д. Объекты представления являются простыми объектами с только примитивами, которые по умолчанию сериализуемы.

Как управлять ФК? Мое личное правило:

Уровень сущности: класс Person и связанный с ним класс Gender

Уровень просмотра: представление «Лицо» со свойствами GenderId и GenderName.

Это означает, что вам нужно расширять свои свойства в примитивы при конвертации для просмотра объектов. Таким образом, ваши объекты json становятся проще и проще в обращении.

Когда вам нужно отправить изменения в БД, в моем случае я использую AutoMapper и создаю класс ValueResolver, который может преобразовать ваш новый Guid в объект Gender.

ОБНОВЛЕНИЕ: Отметьте http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/, чтобы узнать, как напрямую получить представление (AliasToBean) от NH. Это было бы усилением на стороне БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...