.NET 常用ORM之NHibernate


NHibernate做.Net應該都不陌生,今天我們就算是溫故下這個技術,概念性的東西就不說了,這次主要說本人在實際使用的遇到的問題,比較費解現在就當是記錄下,避免以后再犯。本次主要使用的情況是1對N多表關聯操作,具體情況如下(給出主要代碼):

一、NHibernate配置

(1) 引入動態庫

  Antlr3.Runtime.dll、NHibernate.dll、Newtonsoft.Json.dll、Iesi.Collections.dll相關動態庫,用NuGet導入即可

(2)配置文件

  configSections加入NHibernate配置信息

<configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>

       Nhibernate相關配置信息及數據鏈接配置

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory name="Sys.Dal">
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
      <property name="connection.connection_string">Data Source=.;Initial Catalog=AttendanceDB;User ID=sa;Password=******;</property>
      <property name="show_sql">false</property>
      <property name="query.substitutions">true 1false 0, yes 'Y', no 'N'</property>
      <property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
      <property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property>
      <property name="hbm2ddl.keywords">none</property>

      <mapping assembly="Sys.Model" />
    </session-factory>
  </hibernate-configuration>

 

(3)創建創建session工場,方便后面直接使用

public class SessionManager
    {
        private static Configuration _cfg = new Configuration().AddAssembly("Sys.Model");

        private static ISessionFactory _sessionFactory = _cfg.BuildSessionFactory();
        private SessionManager()
        {

        }

        public static ISession GetSession()
        {

            return _sessionFactory.OpenSession();
        }

        public static IStatelessSession GetStateLessSession()
        {
            return _sessionFactory.OpenStatelessSession();
        }
    }

二、實際使用

(1)相關實體和映射文件

User.cs

 public class User
    {
        public virtual int ID { get; set; }
        public virtual string UserName { get; set; }
        public virtual string UserPwd { get; set; }
        public virtual int UserType { get; set; }
        public virtual string Telephone { get; set; }
        public virtual string CreateDate { get; set; }
        public virtual string UpdateDate { get; set; }
        public virtual int Status { get; set; }
        public virtual string Remark { get; set; }

        public virtual ISet<AttendanceInfo> listAttendanceInfo { get; set; }
    }
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Sys.Model.User, Sys.Model" table="Tb_User" lazy="true">
    <id name="ID" column="ID" type="Int32">
      <generator class="identity" />
    </id>

    <property name="UserName" type="String">
      <column name="UserName"/>
    </property>

    <property name="UserPwd" type="String">
      <column name="UserPwd"/>
    </property>

    <property name="UserType" type="Int32">
      <column name="UserType"/>
    </property>

    <property name="Telephone" type="String">
      <column name="Telephone"/>
    </property>

    <property name="CreateDate" type="String">
      <column name="CreateDate"/>
    </property>

    <property name="UpdateDate" type="String">
      <column name="UpdateDate"/>
    </property>

    <property name="Status" type="Int32">
      <column name="Status"/>
    </property>

    <property name="Remark" type="String">
      <column name="Remark"/>
    </property>

    <!-- 一個User有多個Attendance -->
    <set name="listAttendanceInfo" table="Tb_AttendanceInfo" generic="true" inverse="true" >
      <key column="ID" foreign-key="FK_Att_User"/>
      <one-to-many class="Sys.Model.AttendanceInfo, Sys.Model"/>
    </set>
    
  </class>
</hibernate-mapping>

 

AttendanceInfo.cs
public class AttendanceInfo
    {

        public virtual int ID { get; set; }
        public virtual int UID { get; set; }
        public virtual string CheckInTime { get; set; }
        public virtual string CheckOutTime { get; set; }
        public virtual int Status { get; set; }
        public virtual int ConfirmUID { get; set; }
        public virtual string ConfirmTime { get; set; }
        public virtual string Remark { get; set; }
        public virtual User UserInfo { get; set; }
    }
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Sys.Model.AttendanceInfo, Sys.Model" table="Tb_AttendanceInfo" lazy="true">
    <id name="ID" column="ID" type="Int32">
      <generator class="identity" />
    </id>

    <property name="UID" type="Int32">
      <column name="UID"/>
    </property>

    <property name="CheckInTime" type="String">
      <column name="CheckInTime"/>
    </property>

    <property name="CheckOutTime" type="String">
      <column name="CheckOutTime"/>
    </property>

    <property name="Status" type="Int32">
      <column name="Status"/>
    </property>

    <property name="ConfirmUID" type="Int32">
      <column name="ConfirmUID"/>
    </property>

    <property name="ConfirmTime" type="String">
      <column name="ConfirmTime"/>
    </property>
    
    <property name="Remark" type="String">
      <column name="Remark"/>
    </property>

    <many-to-one name="UserInfo" column="UID" not-null="true" class="Sys.Model.User, Sys.Model"  foreign-key="FK_Att_User" />
  </class>
</hibernate-mapping>

(2)獲取數據——原意是獲取AttendanceInfo帶出User上面的個別字段

public List<AttendanceInfo> GetAttendanceInfoByWhere(string uname)
        {
            using (ISession session = SessionManager.GetSession())
            {
                ICriteria criteria = session.CreateCriteria<AttendanceInfo>();
                List<AttendanceInfo> lsAtt = criteria.List<AttendanceInfo>().ToList();
                return lsAtt;
            }

        }

 

調用方法:

public JsonResult GetUserAttInfo()
        {

            if (Session["uid"] != null && Session["uType"] != null)
            {
                string strWhe = Request["strWhe"].ToString();

                Bll_AttendanceInfo bllAtt = new Bll_AttendanceInfo();

                string uname = string.Empty;
                if (Session["uType"].ToString().Equals("1"))
                    uname = Session["uname"].ToString();
                else
                    uname = strWhe;

                List<AttendanceInfo> lsAtt = bllAtt.GetAttendanceInfoByWhere(uname);
                if (lsAtt != null && lsAtt.Count > 0)
                {
                    return Json(JsonHeleper.ObjectToJSON(lsAtt));
                }
                return Json("{\"respCode\":\"faile\",\"respMsg\":\"未獲取到任何行,請稍后重試!\"}");
            }
            else
            {
                return Json("{\"respCode\":\"faile\",\"respMsg\":\"Session丟失,請稍后重試!\"}");
            }
        }
public static string ObjectToJSON<T>(T obj)
        {           
            return JsonConvert.SerializeObject(obj);
           
        }

 

好,到這里看上去配置映射關系好像沒多大問題,但是實際上問題就出來了

 

問題一:在調用的GetAttendanceInfoByWhere的時候會出現:no session or session was closed...

其實是因為做了映射關系先去子表在去主表數據,session釋放掉了;需要在子表的映射文件加上個lazy="false",把延遲加載關掉使用使用代理延遲,那么這個問題解決了。

 

上面的問題是在去取值的時候會拋出異常,接着問題又來了,問題二:Json(JsonHeleper.ObjectToJSON(lsAtt))也就是到最后的JsonConvert.SerializeObject(obj),json序列化出錯,怎么可能系統紫的json序列化方法會出錯,而且這里報的錯和上面的那個錯誤信息基本一致,字面意思也是session不存在或者關掉,納悶,剛剛明明已經處理掉這個問題,為毛在取值的地方沒出現,在返回的json序列化出錯,找了很久其實有時映射文件配置問題,在主表也就是User的映射文件中加上了cascade="all" generic="true",以及在子表映射文件加上 not-found= "ignore" ,就ok了

 

緊接着第三個問題,還是在返回json序列化的時候異常:Self referencing loop detected for property 'ParentClassify' with type...字面意思就是自引用循環,就是主類新加子類的屬性到實體,子類新增主類的屬性到實體,就出了這種問題,怎么解決?調整了下最終的對象轉json的方法ObjectToJSON,需要導入包文件

using Newtonsoft.Json;

 

public static string ObjectToJSON<T>(T obj)
        {    
       
//return JsonConvert.SerializeObject(obj); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; return JsonConvert.SerializeObject(obj, settings); }

 

到此為止,關聯查詢算是沒問題了,但是不要慶幸,這只是查詢,接下來做了下子表的新增和修改操作,結果讓人奔潰,異常!!!!!!

錯誤信息:Error dehydrating property value for...

最終導致這個問題是因為數據庫字段和映射文件字段不一致,對應不起來,因為映射文件加了個關聯對象。需要在加的關聯對象的hbm.xml文件里面加上insert="false" update="false",讓新增和修改的時候忽略掉。開發工具卻一直報但是它一直拋Error dehydrating property value for...誤導排錯方向,這個坑,讓人無語。

最后順便提一嘴,在使用Nhibernate多表關聯的時候,mapping配置還真的小心點,一不小心就是個坑,而且資料不是很好找。

最終兩個hbm.xml的關聯關系配置變成如下:

User.hbm.xml

<!-- 一個User有多個Attendance -->
    <set name="listAttendanceInfo" table="Tb_AttendanceInfo" cascade="all" generic="true" inverse="true"  lazy="false"  >
      <key column="ID" foreign-key="FK_Att_User" not-null="false"/>
      <one-to-many class="Sys.Model.AttendanceInfo, Sys.Model" not-found= "ignore" />
    </set>

AttendanceInfo.hbm.xml

<many-to-one name="UserInfo" column="UID" not-null="false" class="Sys.Model.User, Sys.Model" cascade="none" not-found="ignore" insert="false" update="false" foreign-key="FK_Att_User" />

 

到此幾個坑基本解決,表示很心累。寫出來也后來遇到的人參考,有何不對的地方也歡迎大家指教。

 (另外,需要注意的是建立one-to-many或者many-to-on的欄位需要是主鍵,所以需要留意xml映射文件的書寫)

三、關聯表配置的映射屬性

上面也提到了,好幾個問題都是在做關聯關系配置的時候出的問題,順便附上關聯表配置的映射屬性拱參考

  • access(默認property):可選field、property、nosetter、ClassName值。NHibernate訪問屬性的策略。
  • cascade(可選):指明哪些操作會從父對象級聯到關聯的對象。可選all、save-update、delete、none值。除none之外其它將使指定的操作延伸到關聯的(子)對象。
  • class(默認通過反射得到屬性類型):關聯類的名字。
  • column(默認屬性名):列名。
  • fetch(默認select):可選select和join值,select:用單獨的查詢抓取關聯;join:總是用外連接抓取關聯。
  • foreign-key:外鍵名稱,使用SchemaExport工具生成的名稱。
  • index:......
  • update,insert(默認true):指定對應的字段是否包含在用於UPDATE或INSERT 的SQL語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關聯,它的值是通過映射到同一個(或多個)字段的某些其他特性得到或者通過觸發器其他程序得到。
  • lazy:可選false和proxy值。是否延遲,不延遲還是使用代理延遲。
  • name:屬性名稱propertyName。
  • not-found:可選ignore和exception值。找不到忽略或者拋出異常。
  • not-null:可選true和false值。
  • outer-join:可選auto、true、false值。
  • property-ref(可選):指定關聯類的一個屬性名稱,這個屬性會和外鍵相對應。如果沒有指定,會使用對方關聯類的主鍵。這個屬性通常在遺留的數據庫系統使用,可能有外鍵指向對方關聯表的某個非主鍵字段(但是應該是一個唯一關鍵字)的情況下,是非常不好的關系模型。比如說,假設Customer類有唯一的CustomerId,它並不是主鍵。這一點在NHibernate源碼中有了充分的體驗。
  • unique:可選true和false值。控制NHibernate通過SchemaExport工具生成DDL的過程。
  • unique-key(可選):使用DDL為外鍵字段生成一個唯一約束。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM