2008-11-13 18 views
30

Hay alguien utilizando JSON.NET con NHibernate? Noté que recibo errores cuando intento cargar una clase con colecciones secundarias.JSON.NET y NHibernate Lazy Loading Colecciones de

+2

¿Puede por favor publicar detalles sobre los errores que está viendo? –

+0

Estaba obteniendo 'El método u operación no está implementado'. y la solución de Liedman funcionó para mí. –

Respuesta

3

¿Usted está consiguiendo una dependencia-circular error? ¿Cómo ignoras los objetos de la serialización?

Dado que la carga diferida genera un proxy de objetos, cualquier atributo de clase sus miembros han se perderán. Me encontré con el mismo problema con Newtonsoft JSON-serializer, ya que el proxy-objeto ya no tenía los atributos [JsonIgnore].

+0

Vea las respuestas de Mine y Handcraftsman, contienen una solución para exactamente este problema. – Liedman

+2

Tipo de voto retrasado para votar una respuesta que fue escrita 1,5 años antes que la tuya? – jishi

+0

Sí, ese es exactamente el problema con el que me estaba encontrando. Sin embargo, no había estado ignorando ningún objeto de la serialización cuando estaba recibiendo los errores. ¡Creo que necesito volver y leer los documentos correctamente! – user32326

3

Es probable que desee a la carga ansiosa la mayor parte del objeto de modo que se puede serializar:

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

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

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

Una buena manera de hacer esto es añadir un bool al constructor (bool isFetchEager) de sus datos método del proveedor.

18

utilizo NHibernate con Json.NET y se dio cuenta que estaba recibiendo "__interceptors" inexplicables propiedades en mis objetos serializados. Una búsqueda en google apareció this excellent solution por Lee Henson que adapté para trabajar con Json.NET 3.5 Release 5 de la siguiente manera.

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); 
    } 
} 

Para usarlo, simplemente ponga una instancia en la propiedad ContractResolver de su JsonSerializer. El problema de dependencia circular observado por jishi puede resolverse estableciendo la propiedad ReferenceLoopHandling en ReferenceLoopHandling.Ignore. Aquí es un método de extensión que se puede utilizar para serializar objetos usando 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); 
     } 
    } 
    } 
+2

Gracias por este código, ¡funciona genial! Y me salvó de usar una sesión sin estado. –

+0

Esto dejó de funcionar en JSON.NET 3.5 versión 7, pero aún funciona bien en 3.5 versión 5. – zcrar70

+0

parece que han actualizado AssemblyVersion a partir de la versión 6, lo que significa que si incluyó previamente este archivo en su solución, la versión no coincidiría , y lanzar algún tipo de excepción de seguridad. ¿Podría ser ese el caso? – jishi

23

Hemos tenido este problema exacto, que se resolvió con la inspiración de la respuesta de Handcraftsman aquí.

El problema surge de JSON.NET ser confundido acerca de cómo serializar clases de proxy de NHibernate. Solución: serialice las instancias de proxy como su clase base.

una versión simplificada del código de Handcraftsman dice así:

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); 
     } 
    } 
} 

en mi humilde opinión, este código tiene la ventaja de que siguen confiando en el comportamiento por defecto de JSON.NET con respecto a los atributos personalizados, etc. (y el código es mucho ¡corta!).

Se utiliza como esto

 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(); 

Nota: Este código fue escrito para utilizar con NHibernate y 2.1. Como algunos comentaristas han señalado, no funciona de la caja con las versiones posteriores de NHibernate, tendrá que hacer algunos ajustes. Intentaré actualizar el código si alguna vez tengo que hacerlo con versiones posteriores de NHibernate.

+0

¡Funcionó muy bien para mí! –

+0

No estoy seguro de dónde se usaría este código y cómo. – Ciel

+0

La solución de Liedman ya no funciona porque el tipo pasado es Interfaz, por lo que objectType.BaseType devuelve nulo y se bloquea –

41

Estaba enfrentando el mismo problema, así que traté de usar el código de @ Liedman, pero nunca me llamaron al GetSerializableMembers() para la referencia por proxy. he encontrado otro método para anular:

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); 
     } 
    } 
+3

+1 - Esta parece ser la única versión que funciona ahora con NH 3.3 y JSON.NET 4.5.7. – TheCloudlessSky

+0

Y probablemente quieras usarlo así: JsonConvert.DefaultSettings =() => nuevos JsonSerializerSettings \t \t \t { \t \t \t \t ContractResolver = new NHibernateContractResolver() \t \t \t}; – PandaWood

+0

En realidad, he intentado esto y no funcionó para mí usando JSON .NET 5,06 y NH 3.3 – PandaWood

1

Yo diría que este es un problema de diseño en mi opinión. Debido a que NH hace conexiones a la base de datos debajo de todo y tiene proxys en el medio, no es bueno que la transparencia de su aplicación serialice directamente (y como puede ver, a Json.NET no le gustan para nada).

No debe serializar las entidades en sí, pero debe convertirlas en objetos "ver" u objetos POCO o DTO (como quiera que los llame) y luego serializarlos.

La diferencia es que, aunque la entidad NH puede tener proxies, atributos diferidos, etc. Los objetos de vista son objetos simples con solo primitivas que son serializables de manera predeterminada.

¿Cómo administrar FKs? Mi regla personal es:

nivel de las Entidades: clase Person y con una clase de género asociadas

Ver nivel: opinión de la persona con propiedades GenderId y GenderName.

Esto significa que necesita expandir sus propiedades en primitivas al convertir objetos para ver. De esta forma también sus objetos json son más simples y fáciles de manejar.

Cuando necesite insertar los cambios en la base de datos, en mi caso utilizo AutoMapper y realizo una clase ValueResolver que puede convertir su nuevo Guid al objeto Gender.

ACTUALIZACIÓN: Compruebe http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/ para obtener una vista directa (AliasToBean) de NH. Esto sería un impulso en el lado de DB.

Cuestiones relacionadas