2008-11-13 9 views
30

Ist jemand mit JSON.NET mit nHibernate? Ich merke, dass ich Fehler erhalte, wenn ich versuche, eine Klasse mit untergeordneten Sammlungen zu laden.JSON.NET und nHibernate Lazy Loading von Sammlungen

+2

Können Sie bitte Details zu den Fehlern posten, die Sie sehen? –

+0

Ich bekam "Die Methode oder Operation ist nicht implementiert." und Liedmans Reparatur funktionierte für mich. –

Antwort

3

Erhalten Sie einen zirkulären Abhängigkeitsfehler? Wie ignorierst du Objekte von der Serialisierung?

Da Lazy Loading ein Proxy-Objekt generiert, gehen alle Attribute verloren, die Ihre Klassenmitglieder haben. Ich stieß auf das gleiche Problem mit Newtonsoft JSON-Serializer, da das Proxy-Objekt nicht mehr die [JsonIgnore] -Attribute hatte.

+0

Siehe Antworten von mir und Handwerker, sie enthalten eine Lösung für genau dieses Problem. – Liedman

+2

Art der Zurückhaltung, um eine Antwort zu stimmen, die 1,5 Jahre vor deiner geschrieben wurde? – jishi

+0

Ja genau das ist das Problem, auf das ich stieß. Ich hatte keine Objekte aus der Serialisierung ignoriert, als ich die Fehler bekam. Ich denke, ich muss zurückgehen und die Dokumente richtig lesen! – user32326

3

Sie wahrscheinlich zu eifrig Last meisten des Objekts wollen, so dass sie serialisiert werden kann:

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

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

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

Ein schöner Weg, dies zu tun, ist ein Bool an den Konstruktor (bool isFetchEager) Ihrer Daten hinzufügen Provider-Methode.

18

Ich benutze NHibernate mit Json.NET und bemerkte, dass ich unerklärliche "__interceptors" Eigenschaften in meinen serialisierten Objekten bekam. Eine Google-Suche ergab this excellent solution von Lee Henson, die ich angepasst habe, um mit Json.NET 3.5 Release 5 wie folgt zu arbeiten.

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

es eine Instanz in der ContractResolver Eigenschaft Ihres JsonSerializer setzen gerade zu verwenden. Das Problem der zirkulären Abhängigkeit, das von Jishi festgestellt wird, kann gelöst werden, indem die ReferenceLoopHandling-Eigenschaft auf ReferenceLoopHandling.Ignore gesetzt wird. Hier ist eine Erweiterung Methode, die verwendet werden kann, um Objekte zu serialisiert mit 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

Danke für diesen Code, es funktioniert super! Und rettete mich davor, eine StatelessSession zu benutzen. –

+0

Dies funktioniert nicht mehr in JSON.NET 3.5 Release 7, funktioniert aber immer noch in 3.5 Release 5. – zcrar70

+0

Sie scheinen die AssemblyVersion ab Release 6 aktualisiert zu haben, was bedeutet, dass die Version nicht übereinstimmen würde, wenn Sie diese Datei zuvor in Ihre Lösung aufgenommen haben , und werfen Sie eine Art Sicherheitsexzeption. Könnte das der Fall sein? – jishi

23

Wir hatten genau dieses Problem, das mit der Inspiration hier aus Handcraftsman Antwort gelöst wurde.

Das Problem tritt auf, wenn JSON.NET verwirrt darüber ist, wie NHibernate-Proxy-Klassen serialisiert werden. Lösung: Serialisieren Sie die Proxy-Instanzen wie ihre Basisklasse.

Eine vereinfachte Version von Code Handcraftsman die geht so:

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, dieser Code den Vorteil, immer noch unter Berufung auf JSON.NET Standardverhalten in Bezug auf benutzerdefinierte Attribute hat usw. (und der Code ist viel kürzer!).

Es ist wie dieser

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

Hinweis verwendet: Dieser Code für und mit NHibernate 2.1 verwendet geschrieben wurde. Wie einige Kommentatoren darauf hingewiesen haben, funktioniert es mit späteren Versionen von NHibernate nicht ohne weiteres, Sie müssen einige Anpassungen vornehmen. Ich werde versuchen, den Code zu aktualisieren, wenn ich es jemals mit späteren Versionen von NHibernate machen muss.

+0

Arbeitete gut für mich! –

+0

Ich bin verwirrt, wo dieser Code verwendet werden würde und wie. – Ciel

+0

Die Lösung von Liedman funktioniert nicht mehr, da der übergebene Typ Interface ist, also gibt objectType.BaseType null zurück und stürzt ab –

41

Ich hatte das gleiche Problem, also versuchte ich @ Liedman's Code zu verwenden, aber die GetSerializableMembers() wurde nie für die Proxy-Referenz aufgerufen. fand ich eine andere Methode außer Kraft zu setzen:

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 - Dies scheint die einzige funktionierende Version jetzt mit NH 3.3 und JSON.NET 4.5.7 zu sein. – TheCloudlessSky

+0

Und Sie möchten es wahrscheinlich so verwenden: JsonConvert.Defaultsettings =() => new JsonSerializerSettings \t \t \t { \t \t \t \t ContractResolver = new NHibernateContractResolver() \t \t \t}; – PandaWood

+0

Eigentlich habe ich versucht, diese und es nicht für mich mit JSON .NET 5.06 und NH 3.3 – PandaWood

1

Ich würde sagen, das ist ein Design-Problem meiner Meinung nach. Da NH Verbindungen zu der Datenbank unter allen herstellt und Proxys in der Mitte hat, ist es nicht gut für die Transparenz Ihrer Anwendung, sie direkt zu serialisieren (und wie Sie sehen, dass Json.NET sie überhaupt nicht mag).

Sie sollten die Entitäten selbst nicht serialisieren, aber Sie sollten sie in "Ansichts" -Objekte oder POCO- oder DTO-Objekte (was immer Sie sie nennen wollen) konvertieren und diese dann serialisieren.

Der Unterschied besteht darin, dass die NH-Entität zwar Proxies, Lazy-Attribute usw. haben kann. View-Objekte sind einfache Objekte mit nur Grundelementen, die standardmäßig serialisierbar sind.

Wie verwalte ich FKs? Meine persönliche Regel ist:

Ebene Entity: Klasse Person und mit einer Gender-Klasse

Ansicht Ebene zugeordnet: Person Ansicht mit GenderId und GenderName Eigenschaften.

Dies bedeutet, dass Sie Ihre Eigenschaften in Grundelemente erweitern müssen, wenn Sie Objekte anzeigen. Auf diese Weise sind auch Ihre json-Objekte einfacher und einfacher zu handhaben.

Wenn Sie die Änderungen an die Datenbank übertragen müssen, verwende ich in meinem Fall AutoMapper und eine ValueResolver-Klasse, die Ihre neue Guid in das Gender-Objekt konvertieren kann.

UPDATE: Überprüfen Sie http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/ für eine Möglichkeit, die Ansicht direkt (AliasToBean) von NH zu bekommen. Dies wäre ein Schub für die DB-Seite.