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

JSON.NET и nHibernate Lazy Загрузка коллекций

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

4b9b3361

Ответ 1

У меня возникла такая же проблема, поэтому я попытался использовать код @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);
      }
  }

Ответ 2

У нас была эта точная проблема, которая была решена с помощью ответа от Handcraftsman здесь.

Проблема возникает из-за того, что 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);
        }
    }
}

IMHO, этот код имеет то преимущество, что он все еще полагается на поведение по умолчанию 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.

Ответ 3

Я использую NHibernate с Json.NET и замечаю, что я получаю необъяснимые свойства "__interceptors" в моих сериализованных объектах. Поиск Google оказался это отличное решение от Lee Henson, которое я адаптировал для работы с 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);
      }
    }
  }

Ответ 4

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

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

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

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

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

Уровень просмотра: просмотр персонажа с свойствами GenderId и GenderName.

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

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

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