许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  FluentNHibernate基本映射详解

FluentNHibernate基本映射详解

阅读数 1
点赞 0
article_banner

最近在做项目的时候用到了NHibernate,使用它并不困难,但是很麻烦。如果我的数据库有几百张表如果想要一个个的映射岂不是很麻烦,所以这种情况下使用NHibernate就会很笨重,虽然这个ORM框架本身功能强大,但属于重量级的在使用的时候太笨重了,所以需要在项目中改良。这时候就应运而生了FluentNHibernate,它是流畅版的NHibernate,支持所有的NHibernate功能,而且还封装了配置文件的映射功能,也就是说可以将映射使用C#代码编写,这样在维护时就会很简单。

          在没有FluentNHibernate的情况下,如果使用NHibernate来做数据库映射,那么首先需要安装NHibernate(也就是应用Nhibernate.dll),然后创建Nhibernate.cfg.xml数据库配置文件,然后创建映射文件.xml,最后创建Session,直接对对象操作即可。虽然这样做并不困难,但是很麻烦,想象下如果数据库表有上百张,那使用这种方法映射不就很麻烦,笨重了吗。

          那么FluentNHibernate有什么好处呢,它能够省略创建映射文件.xml,使用C#代码编写映射文件,这样做能在一定情况下简化工作量,同时也便于对映射代码进行修改,具体 使用方法 接下来会详细讨论。


一、创建数据库配置文件


        首先创建一个数据库的配置文件,刚开始使用的话手动编写太麻烦,这时候可以考虑使用自带的配置文件,在官网下载后会有一个名为Configuration_Templates的文件夹,里面有不同数据库的配置文件,可以使用它的默认设置,但是需要将名称改为Nhibernate.cfg.xml。这里使用如下的配置:

[html] view plaincopyprint?


  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!-- This is the System.Data.dll provider for SQL Server -->  
  3. <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">  
  4.  <session-factory name="KaddzvoteNHibernateFactory">  
  5.    <property name="connection.driver_class">  
  6.      NHibernate.Driver.SqlClientDriver  
  7.    </property>  
  8.    <property name="connection.connection_string">  
  9.           Data Source=.;Initial Catalog=Mapping;Integrated Security=true;Pooling=True;Min Pool Size=20;Max Pool Size=60  
  10.    </property>  
  11.    <property name="dialect">  
  12.      NHibernate.Dialect.MsSql2005Dialect  
  13.    </property>  
  14.    <property name="current_session_context_class">thread_static</property>  
  15.    <property name="generate_statistics">true</property>  
  16.    <property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property>  
  17.    <property name="query.substitutions">  
  18.      true 1, false 0, yes 'Y', no 'N'  
  19.    </property>  
  20.   <!--配置是否显示sql语句,true代表显示-->  
  21.    <property name="show_sql">true</property>  
  22.  </session-factory>  
  23. </hibernate-configuration>  
<?xml version="1.0" encoding="utf-8"?><!-- This is the System.Data.dll provider for SQL Server --><hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">  <session-factory name="KaddzvoteNHibernateFactory">    <property name="connection.driver_class">      NHibernate.Driver.SqlClientDriver    </property>    <property name="connection.connection_string">           Data Source=.;Initial Catalog=Mapping;Integrated Security=true;Pooling=True;Min Pool Size=20;Max Pool Size=60    </property>    <property name="dialect">      NHibernate.Dialect.MsSql2005Dialect    </property>    <property name="current_session_context_class">thread_static</property>    <property name="generate_statistics">true</property>    <property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property>    <property name="query.substitutions">      true 1, false 0, yes 'Y', no 'N'    </property>   <!--配置是否显示sql语句,true代表显示-->    <property name="show_sql">true</property>  </session-factory></hibernate-configuration>



二、创建实体




           NHibernate的基本映射和 Hibernate  是完全相同的,有关基本的映射这里不再详细的讨论,可以翻阅笔者的前几篇文章。下面自己做了一个小的项目Demo,演示如何使用NHibernate创建一个数据库的映射,具体的数据库结构图如下:


      上图的数据库结构图中涵盖了基本的映射关系,在实际的项目中也就是上面出现的几种基本的关系,其中涵盖了一对一、多对一、多对多的关联关系,接下来将会使用FluentNHibernate来实现基本的映射关系。


 2.1 创建实体




           添加完配置文件后使用第三方工具将数据库表导出为实体对象,也就是添加数据库表的实体对象。添加完成后继续添加数据库的映射类,添加映射类时需要继承NHibernate的ClassMap<T>类,将数据库实体放置到对象内部,这样在映射时能够直接使用,它使用的是泛型来实现的。数据库表的实体如下代码:

[csharp] view plaincopyprint?


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Collections.ObjectModel;  
  4. using System.Linq;  
  5. using System.Text;  
  6. using System.Threading.Tasks;  
  7. using FluentNHibernate.Automapping;  
  8. using FluentNHibernate.Conventions;  
  9. using NHibernate;  
  10. using NHibernate.Collection.Generic;  
  11.  
  12.  
  13. namespace ClassLibrary1.mapping  
  14. {  
  15.      
  16.    public abstract class Entity  
  17.    {  
  18.        virtual public int ID { get; set; }  
  19.    }  
  20.  
  21.    public class User : Entity  
  22.    {  
  23.        virtual public string Name { get; set; }  
  24.        public virtual string No { get; set; }  
  25.  
  26.        public virtual UserDetails UserDetails { get; set; }  
  27.    }  
  28.  
  29.    public class Project : Entity  
  30.    {  
  31.        public Project()  
  32.        {  
  33.            Task=new List<Task>();  
  34.            Product=new List<Product>();  
  35.        }  
  36.  
  37.        public virtual string Name { get; set; }  
  38.        public virtual User User { get; set; }  
  39.  
  40.        public virtual IList<Product> Product { get; set; }  
  41.        public virtual IList<Task> Task{get;protected set; }  
  42.  
  43.    }  
  44.  
  45.    public class Product : Entity  
  46.    {  
  47.        public Product()  
  48.        {  
  49.            Project=new List<Project>();  
  50.        }  
  51.  
  52.        public virtual IList<Project> Project { get; set; }  
  53.        public virtual string Name { get; set; }  
  54.        public virtual string Color { get; set; }  
  55.    }  
  56.  
  57.    public class Task : Entity  
  58.    {  
  59.        public virtual string Name { get; set; }  
  60.        public virtual Project Project { get; set; }  
  61.    }  
  62.  
  63.    public class UserDetails : Entity  
  64.    {  
  65.        public virtual User User { get; set; }  
  66.        public virtual int Sex { get; set; }  
  67.        public virtual int Age { get; set; }  
  68.        public virtual DateTime BirthDate { get; set; }  
  69.        public virtual decimal Height { get; set; }  
  70.    }  
  71. }  
using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Text;using System.Threading.Tasks;using FluentNHibernate.Automapping;using FluentNHibernate.Conventions;using NHibernate;using NHibernate.Collection.Generic;  namespace ClassLibrary1.mapping{        public abstract class Entity    {        virtual public int ID { get; set; }    }     public class User : Entity    {        virtual public string Name { get; set; }        public virtual string No { get; set; }         public virtual UserDetails UserDetails { get; set; }    }     public class Project : Entity    {        public Project()        {            Task=new List<Task>();            Product=new List<Product>();        }         public virtual string Name { get; set; }        public virtual User User { get; set; }         public virtual IList<Product> Product { get; set; }         public virtual IList<Task> Task{get;protected set; }     }     public class Product : Entity    {        public Product()        {            Project=new List<Project>();        }         public virtual IList<Project> Project { get; set; }         public virtual string Name { get; set; }        public virtual string Color { get; set; }    }     public class Task : Entity    {        public virtual string Name { get; set; }        public virtual Project Project { get; set; }    }     public class UserDetails : Entity    {        public virtual User User { get; set; }        public virtual int Sex { get; set; }        public virtual int Age { get; set; }        public virtual DateTime BirthDate { get; set; }        public virtual decimal Height { get; set; }    }}



三、映射详解




           在添加映射文件时需要继承FluentNHibernate的ClassMap<T>类,然后在类的构造函数中添加映射的方法,具体的方法是使用的lamuda表达式来映射的,方法的名称跟配置文件的名称基本一致,书写也很方便,接下来将会拆分映射来详细讲解FluentNHibernate的Mapping使用方法。

 3.1 一对一映射




           首先来看看一对一的映射关系,用户和用户 信息 表在实际中是一对一的关系,这两个表之间是通过使用UserID来相互关联的,它们有一个共同的ID,在插入Users表的同时也要写入UserDetails表,所以需要添加一对一的限制关系,具体的在Users和UsersDetails两表的映射方法如下代码:

[csharp] view plaincopyprint?


  1. public class UsersMapping : ClassMap<User>  
  2. {  
  3.    public UsersMapping()  
  4.    {  
  5.        Table("Users");  
  6.        LazyLoad();  
  7.        Id(x => x.ID).Column("UserID").GeneratedBy.Identity();  
  8.        HasOne(x => x.UserDetails).Cascade.All().PropertyRef("User");  
  9.        Map(x => x.Name).Nullable();  
  10.        Map(x => x.No).Nullable();  
  11.  
  12.    }  
  13. }  
public class UsersMapping : ClassMap<User>{    public UsersMapping()    {        Table("Users");        LazyLoad();        Id(x => x.ID).Column("UserID").GeneratedBy.Identity();        HasOne(x => x.UserDetails).Cascade.All().PropertyRef("User");        Map(x => x.Name).Nullable();        Map(x => x.No).Nullable();     }}


Note:FluentNHibernate在映射时有很多种映射方法,比如Cascade它是指该对象在进行操作时关联到的子对象的操作类型,上面指定了All说明所有的操作都会关联到子表,还有SaveUpdate在添加和更新时关联子表,另外还有None类型不推荐使用此类型因为会出现很多问题。


           UserDetails的映射中的主键ID是继承自User类的所以在指定ID时需要添加Foreign外键关联的属性名,内部的参数一定要是UserDetails的属性名。


[csharp] view plaincopyprint?


  1. public class UserDetailsMapping : ClassMap<UserDetails>  
  2. {  
  3.    public UserDetailsMapping()  
  4.    {  
  5.        Table("UserDetails");  
  6.        LazyLoad();  
  7.        Id(x => x.ID).Column("UserID").GeneratedBy.Foreign("User");  
  8.        Map(x => x.Height).Nullable();  
  9.        Map(x => x.Age).Nullable();  
  10.        Map(x => x.Sex).Nullable();  
  11.        Map(x => x.BirthDate).Nullable();  
  12.        HasOne(x => x.User).Cascade.All();  
  13.    }  
  14. }  
public class UserDetailsMapping : ClassMap<UserDetails>{    public UserDetailsMapping()    {        Table("UserDetails");        LazyLoad();        Id(x => x.ID).Column("UserID").GeneratedBy.Foreign("User");        Map(x => x.Height).Nullable();        Map(x => x.Age).Nullable();        Map(x => x.Sex).Nullable();        Map(x => x.BirthDate).Nullable();        HasOne(x => x.User).Cascade.All();    }}



          使用测试方法查看映射结果,具体的测试方法如下:


[csharp] view plaincopyprint?


  1. using System;  
  2. using System.Collections.Generic;  
  3. using ClassLibrary1.mapping;  
  4. using FluentNHibernate.Testing;  
  5. using ClassLibrary1;  
  6. using NHibernate;  
  7. using NUnit.Framework;  
  8. namespace UnitTestProject1  
  9. {  
  10.    [TestFixture]  
  11.    public class UnitTest1:NHConfig  
  12.    {  
  13.        [Test]  
  14.        public void TestUsers_UserDetails()  
  15.        {  
  16.            //get user from database  
  17.            User user1 = Session.Load<User>(1);  
  18.  
  19.            //save the User data  
  20.            Session.Transaction.Begin();  
  21.            User user=new User()  
  22.            {  
  23.                Name = "Jack",  
  24.                No = "12321"  
  25.            };  
  26.            UserDetails userDetails=new UserDetails()  
  27.            {  
  28.                Age = 12,  
  29.                BirthDate = DateTime.Now.Date,  
  30.                Height = 240,  
  31.                Sex = 1  
  32.            };  
  33.            user.UserDetails = userDetails;  
  34.            userDetails.User = user;  
  35.            Session.Save(user);  
  36.            Session.Transaction.Commit();  
  37.        }  
  38.    }  
  39. }  
using System;using System.Collections.Generic;using ClassLibrary1.mapping;using FluentNHibernate.Testing;using ClassLibrary1;using NHibernate;using NUnit.Framework;namespace UnitTestProject1{    [TestFixture]    public class UnitTest1:NHConfig    {        [Test]        public void TestUsers_UserDetails()        {            //get user from database            User user1 = Session.Load<User>(1);             //save the User data            Session.Transaction.Begin();            User user=new User()            {                Name = "Jack",                No = "12321"            };            UserDetails userDetails=new UserDetails()            {                Age = 12,                BirthDate = DateTime.Now.Date,                Height = 240,                Sex = 1            };            user.UserDetails = userDetails;            userDetails.User = user;            Session.Save(user);            Session.Transaction.Commit();        }    }}



          在 get  和save对象的地方添加断点,Debug运行测试就会看到执行的结果。


 3.2 一对多/多对一




           一对多和多对一是相对而言的正如上例中的Projects和Tasks类似,一个Projects有多个Tasks,反过来说就是多个Tasks可能会对应一个Projects所以有时一对多的关系也就是多对一的关系,只不过是一种特殊的多对一。这里的多对一比较特殊,常见的多对一的关系比如学生和班级的关系,多个学生属于一个班级。

            一对多的映射方法和一对一的映射方法其实很多地方是类似的,只不过一对多的关系里面要添加一个外键引用关系,然后在多的一端添加一个外键,在一的一端添加HasMany,映射到Projects和Tasks中就是在Tasks(多)中添加Project的外键。

  3.2.1 映射

        首先时Tasks表的映射,因为Tasks表是多的一端,所以要添加对Projects表的外键引用关系,另外因为是一种外键引用不关系到父表的操作,所以这里可以使用Cascade.None()。

[csharp] view plaincopyprint?


  1. public class TasksMappping : ClassMap<ClassLibrary1.mapping.Task>  
  2. {  
  3.    public TasksMappping()  
  4.    {  
  5.        Table("Tasks");  
  6.        LazyLoad();  
  7.        Id(x => x.ID).Column("TaskID").GeneratedBy.Identity();  
  8.        References(x => x.Project).Nullable().Column("ProjectID").Cascade.None();  
  9.        Map(x => x.Name).Nullable();  
  10.    }  
  11. }  
public class TasksMappping : ClassMap<ClassLibrary1.mapping.Task>{    public TasksMappping()    {        Table("Tasks");        LazyLoad();        Id(x => x.ID).Column("TaskID").GeneratedBy.Identity();        References(x => x.Project).Nullable().Column("ProjectID").Cascade.None();        Map(x => x.Name).Nullable();    }}



          在Projects表中,因为该表中的一个ID会对应多个Tasks所以在添加HasMany方法,来表明Projects和Tasks的多对一的关系,如下代码它会涉及到任务的添加和更新操作,所以需要使用Cascade.SaveUpdate()。


[csharp] view plaincopyprint?


  1. public class ProjectsMapping:ClassMap<Project>  
  2. {  
  3.    public ProjectsMapping()  
  4.    {  
  5.        Table("Projects");  
  6.        LazyLoad();  
  7.        Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();  
  8.        References(x => x.User).Column("UserID").Cascade.None();  
  9.        Map(x => x.Name).Nullable();  
  10.        HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();  
  11.     }  
  12. }  
public class ProjectsMapping:ClassMap<Project>{    public ProjectsMapping()    {        Table("Projects");        LazyLoad();        Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();        References(x => x.User).Column("UserID").Cascade.None();        Map(x => x.Name).Nullable();        HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();     }}


Note:TasksMapping中的外键关系使用的是Cascade.None这说明它的操作不会涉及到Projects的操作,但是ProjectsMapping中使用了Cascade.SaveUpdate()方法所以在save或者update Projects的时候会连带着修改Tasks。


 3.2.2 Unit Test

     编写单元测试代码如下:


[csharp] view plaincopyprint?


  1. [Test]  
  2. public void TestOneToMany()  
  3. {  
  4.    Project project = Session.Get<Project>(15);  
  5.  
  6.    //save the User data  
  7.    Session.Transaction.Begin();  
  8.    Task task = new Task()  
  9.    {  
  10.        Name ="create",  
  11.        Project = project  
  12.    };  
  13.    Session.Save(task);  
  14.    Session.Transaction.Commit();  
  15.  
  16.    Task task1 = Session.Get<Task>(1);  
  17. }  
[Test]public void TestOneToMany(){    Project project = Session.Get<Project>(15);     //save the User data    Session.Transaction.Begin();    Task task = new Task()    {        Name ="create",        Project = project    };    Session.Save(task);    Session.Transaction.Commit();     Task task1 = Session.Get<Task>(1);}



 执行查看结果:


      这里使用的一对多的关联只是单向的关联,在关联中不仅有单向的另外还有双向关联,具体使用方法这里不再详细讨论,有兴趣学习的话可以翻阅笔者的前篇文章有关Hibernate的关联关系。

 3.3 多对多


       上文详细讨论了一对一、多对一/一对多的关系,使用FluentNHibernate来映射这种关系就很简单了,最后继续讨论多对多的关系,多对多的关系在使用的时候更类似于一对一的关系,因为它属于双向的关联,所以需要在关联的两端同时添加HasManyToMany的映射方法,另外反应到数据库中这其实是需要建立关联表,利用第三张表来维护双向的关系,具体的使用方法如下实例。



   3.3.1 映射


       在项目中常见的多对多的关系有很多,比如本例中使用的Product和Project的关系,一个Project会有有很多Product,同时一个Product也可能会在多个Project中,它们之间就形成了多对多的关联关系。反映到映射关系中,代码如下:

[csharp] view plaincopyprint?


  1. public class ProjectsMapping:ClassMap<Project>  
  2. {  
  3.    public ProjectsMapping()  
  4.    {  
  5.        Table("Projects");  
  6.        LazyLoad();  
  7.        Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();  
  8.        References(x => x.User).Column("UserID").Cascade.None();  
  9.        Map(x => x.Name).Nullable();  
  10.        HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();  
  11.        HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct");  
  12.    }  
  13. }  
  14.  
  15. public class ProductMapping : ClassMap<Product>  
  16. {  
  17.    public ProductMapping()  
  18.    {  
  19.        Table("Product");  
  20.        Id(x => x.ID).Column("ProductID").GeneratedBy.Identity();  
  21.        Map(x => x.Name).Nullable();  
  22.        Map(x => x.Color).Nullable();  
  23.        HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct");  
  24.    }  
  25. }  
public class ProjectsMapping:ClassMap<Project>{    public ProjectsMapping()    {        Table("Projects");        LazyLoad();        Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();        References(x => x.User).Column("UserID").Cascade.None();        Map(x => x.Name).Nullable();        HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();        HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct");    }} public class ProductMapping : ClassMap<Product>{    public ProductMapping()    {        Table("Product");        Id(x => x.ID).Column("ProductID").GeneratedBy.Identity();        Map(x => x.Name).Nullable();        Map(x => x.Color).Nullable();        HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct");    }}



          具体添加关联的步骤如下:

                  (1)在映射的两端同时添加HasManyToMany的关系这样就形成了双向的关联关系

                  (2)指定映射的ParentKey和ChildKey,一般会将对象本身的ID指定为ParentKey,关联对象的ID指定为ChildKey

                  (3)指定关联关系的关系表,使用Table方法指定关联表,如上示例的Table("ProjectProduct")。



 3.3.2  Unit Test

      最后添加一个测试方法来查看映射的结果,是否实现了多对多的映射关系,具体测试方法如下。


[csharp] view plaincopyprint?


  1. [Test]  
  2. public void TestManyToMany()  
  3. {  
  4.    Session.Transaction.Begin();  
  5.    //get the Project  
  6.    ICriteria query = Session.CreateCriteria<Project>();  
  7.    IList<Project> projects = query.List<Project>();  
  8.  
  9.    //create the Product  
  10.    Product product=new Product()  
  11.    {  
  12.        Name = "Product1",  
  13.        Color = "Red"  
  14.    };  
  15.    product.Project = projects;  
  16.    Session.Save(product);  
  17.    Session.Transaction.Commit();  
  18. }  
[Test]public void TestManyToMany(){    Session.Transaction.Begin();    //get the Project    ICriteria query = Session.CreateCriteria<Project>();    IList<Project> projects = query.List<Project>();     //create the Product    Product product=new Product()    {        Name = "Product1",        Color = "Red"    };    product.Project = projects;    Session.Save(product);    Session.Transaction.Commit();}



            Debug运行测试方法,运行到get projects处查看projects所获取的对象信息如下图:



        从上图可以看出已经获取到了与projects相关联的Product,这就是多对多的关系,在获取projects时同时获取了与它关联的Products,如果这里使用Lazyload方式的话就不会获取所有的信息,所以要根据具体的情况而定。


           继续运行测试,成功执行。



          查看数据库发现数据已经成功添加,如下图:


结语


        本文主要讨论了FluentNHibernate的基本使用技巧,突出讨论了一对一的双向关联映射,一对多的单向关联和多对多的双向关联关系,它们使用相当简单,因为有了FluentNHibernate,只需要了解关联的规则就可以了,从数据模型到对象模型真的就很简单了。

        虽然使用FluentNHibernate在一定程度上减少了编写代码,但是并不能真正的解决代码冗余的繁琐问题,可否有一中不需要编写Mapping代码的方法来实现映射关系呢?是的FluentNHibernate还封装了一种AutoMapping方式来映射对象,是一种自动映射的方法,只需要继承实现数据库表到对象的转换规则就可以了,具体的实现方法将会在下篇文章中详细讨论。


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删


相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空