2008-11-13 116 views
30

是否有人使用JSON.NET與nHibernate?我注意到當我嘗試使用子集合加載類時出現錯誤。JSON.NET和nHibernate延遲加載集合

+2

可否請您發佈有關您所看到的錯誤的詳細信息? – 2008-11-13 20:30:10

+0

我正在'方法或操作未執行'。並且Liedman的修復爲我工作。 – 2011-10-14 14:51:19

回答

3

你是否得到一個循環依賴性錯誤?你如何忽略序列化的對象?

由於延遲加載會生成代理對象,因此您的類成員擁有的任何屬性都將丟失。我遇到了與Newtonsoft JSON序列化程序相同的問題,因爲代理對象不再具有[JsonIgnore]屬性。

+0

看到我的和Handcraftsman的回覆,他們包含一個解決方案,正是這個問題。 – Liedman 2010-09-20 08:55:53

+2

一種遲到的投票回答是在你的1,5年之前寫的嗎? – jishi 2010-09-21 13:03:21

+0

是的,這正是我遇到的問題。當我得到錯誤時,我沒有忽略任何來自序列化的對象。我想我需要回去仔細閱讀文檔! – user32326 2008-11-16 13:55:19

3

你可能會想急於負荷大部分對象,以便它可以被序列化:

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

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

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

一個很好的方式做,這是一個布爾添加到構造(布爾isFetchEager)數據的提供者方法。

18

我使用NHibernate和Json.NET,並注意到我在序列化對象中出現莫名其妙的「__interceptors」屬性。谷歌搜索由Lee Henson翻譯成了this excellent solution,我修改瞭如下的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); 
    } 
} 

要使用它,只是把一個實例在JsonSerializer的ContractResolver財產。通過將ReferenceLoopHandling屬性設置爲ReferenceLoopHandling.Ignore可以解決由jishi記錄的循環依賴關係問題。下面是可以用於使用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); 
     } 
    } 
    } 
23

我們有這個確切的問題,這是解決了這裏的靈感來自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); 
     } 
    } 
} 

恕我直言,這個代碼有仍然依靠關於自定義屬性,等等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的更高版本來完成。

41

我面臨同樣的問題,所以我試圖使用@ 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); 
     } 
    } 
1

我會說這在我看來是一個設計問題。由於NH與所有數據庫連接,中間有代理,所以它不利於應用程序直接序列化它們的透明性(正如你可以看到的,Json.NET根本不喜歡它們)。

你不應該序列化實體本身,但你應該將它們轉換成「視圖」對象或POCO或DTO對象(無論你想調用它們),然後將它們序列化。

不同之處在於,雖然NH實體可能具有代理,惰性屬性等。查看對象是隻有基本類型的簡單對象,默認情況下它們是可序列化的。

如何管理FK? 我個人的原則是:

實體一級:Person類和關聯

查看水平性別類:與GenderId和GenderName性個人檢視。

這意味着在轉換爲視圖對象時需要將屬性擴展爲基元。這樣,你的json對象也更簡單,更容易處理。

當您需要將更改推送到數據庫時,我使用AutoMapper並執行一個ValueResolver類,它可以將您的新Guid轉換爲Gender對象。

更新:檢查http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/獲取NH中直接獲取視圖(AliasToBean)的方法。這將會對數據庫方面起到推動作用。