NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)
摘要
从这一节起,介绍NHibernate Mapping的内容。前面文章都是使用的NHibernate XML Mapping。NHibernate XML Mapping是NHibernate最早最成熟的Mapping方法。其他的Mapping方法都是基于XML Mapping的思想进行的“变种”,要么暂时不能完全像XML Mapping那样功能丰富。其他的Mapping方法目前包括:Fluent Mapping、Attribute Mapping和Mapping by Conventions。他们各自都有优缺点。使用者应该根据实际情况选择适合自己项目的Mapping方式。
这篇文章介绍Fluent Mapping。本篇文章的代码可以到Fluent NHibernate下载。
1、Fluent Mapping的优点
- Fluent Mapping提供了大量的Fluent API进行映射配置。相比XML Mapping,在代码中进行配置能够在编译时发现很多问题。
- Fluent Mapping的可读性更强,代码更简洁。
- Fluent Mapping将映射配置的类和实体映射类相分离,在一定程度上保持了实体类的简洁性。
- Fluent Mapping使用Lamda表达式和静态类型反射技术,不用写大量常量字符串,避免了很多粗心的错误。
2、Fluent Mapping的缺点
- 在定义了实体类之后,需要另外定义一个实体映射类。
- 许多XML Mapping支持的功能,Fluent Mapping暂时不支持,需要等到Fluent Mapping新版本出来后才能支持。
- Fluent Mapping底层其实还是将代码定义的映射翻译成XML映射文件,因此在程序启动的时候比XML Mapping稍慢。
- 如果数据库表名称和实体类名称不一致,或者数据库列名称和属性名称不一致,还是需要用字符串的形式做映射,这基本是避免不了的。
3、程序演示
继续使用以之前文章使用过的NHibernateDemoDB数据库。
1)新建工程Demo.Fluent。
2)新建Class Library,名称为Demo.Fluent.Entities。移除Class1.cs文件。
3)在新建的工程中,使用NuGet安装FluentNHibernate。
单击“Install”按钮,会出现Priview对话框,列出将要添加的引用。安装FluentNHibernate将会安装他所依赖的NHibernate和Isesi.Collections。
点击“OK”按钮。等上几分钟时间去喝口茶, 安装完成之后Output将显示Finished。
展开工程的Reference,看到已经将FluentNHibernate添加进来了。
4)添加Domain文件夹和Mapping文件夹。
5)在Domain文件夹内,添加实体类的抽象泛型基类Entity。
namespace Demo.Fluent.Entities.Domain
{
public abstract class Entity<T> where T : Entity<T>
{
public virtual int Id { get; private set; } public override bool Equals(object obj)
{
var other = obj as T;
if (other == null) return false;
var thisIsNew = Equals(Id, );
var otherIsNew = Equals(other.Id, );
if (thisIsNew && otherIsNew)
{
return ReferenceEquals(this, other);
}
return Id.Equals(other.Id);
} private int? oldHashCode;
public override int GetHashCode()
{
// once we have a hashcode we'll never change it
if (oldHashCode.HasValue)
{
return oldHashCode.Value;
}
// when this instance is new we use the base hash code
// and remember it, so an instance can NEVER change its
// hash code.
var thisIsNew = Equals(Id, );
if (thisIsNew)
{
oldHashCode = base.GetHashCode();
return oldHashCode.Value;
}
return Id.GetHashCode();
} public static bool operator ==(Entity<T> lhs, Entity<T> rhs)
{
return Equals(lhs, rhs);
}
public static bool operator !=(Entity<T> lhs, Entity<T> rhs)
{
return !Equals(lhs, rhs);
}
}
}
- 抽象基类Entity定义了实体类共有的Id属性。
- 抽象基类Entity重写了object类的Equals方法和GetHashCode方法,同时重载了运算符==和!=。
6)在Domain文件夹下,添加值对象类Address类、Name类,实体类:Customer类、Product类和Order类。
Address类
namespace Demo.Fluent.Entities.Domain
{
public class Address
{
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; } public bool Equals(Address other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Street, Street) &&
Equals(other.City, City) &&
Equals(other.Province, Province) &&
Equals(other.Country, Country);
} public override bool Equals(object obj)
{
return Equals(obj as Address);
} public override int GetHashCode()
{
unchecked
{
var result = Street.GetHashCode();
result = (result * ) ^ (City != null ? City.GetHashCode() : );
result = (result * ) ^ Province.GetHashCode();
result = (result * ) ^ Country.GetHashCode();
return result;
}
}
}
}
Name类
using System; namespace Demo.Fluent.Entities.Domain
{
public class Name
{
public string LastName { get; private set; }
public string FirstName { get; private set; } public Name() { } public Name(string firstName, string lastName)
{
if (string.IsNullOrWhiteSpace(firstName))
{
throw new ArgumentException("First name must be defined.");
}
if (string.IsNullOrWhiteSpace(lastName))
{
throw new ArgumentException("Last name must be defined.");
}
FirstName = firstName;
LastName = lastName;
} public override int GetHashCode()
{
unchecked
{
var result = FirstName.GetHashCode();
result = (result * ) ^ LastName.GetHashCode();
return result;
}
} public bool Equals(Name other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.FirstName, FirstName) &&
Equals(other.LastName, LastName);
} public override bool Equals(object other)
{
return Equals(other as Name);
}
}
}
Address和Name两个类注意两点:
- 重写了object类的Equals方法和GetHashCode方法。
- 因为是值对象类型,因此不继承Entity类。
Customer类
using System;
using System.Collections.Generic; namespace Demo.Fluent.Entities.Domain
{
public class Customer : Entity<Customer>
{
public Customer()
{
MemberSince = DateTime.UtcNow;
} public virtual Name Name { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Address Address { get; set; } private readonly IList<Order> orders;
public virtual IList<Order> Orders
{
get
{
return orders;
}
}
} public enum CustomerCreditRating
{
Excellent, VeryVeryGood, VeryGood, Good, Neutral, Poor, Terrible
}
}
这里有六个需要注意的地方:
- Customer类继承泛型类Entity<Customer>。
- 不用再在Customer类里定义Id属性。
- 必须有一个无参数的构造函数,可以在这个构造函数中定义属性的默认值。
- 集合属性类型必须定义成接口类型,Fluent NHibernate通过反射生成Fluent对应的集合类型。
- 不能在构造函数中对集合属性进行初始化。
- 所有成员函数(如果有的话)和成员属性都以virtual修饰。
Product类
using System.Collections.Generic; namespace Demo.Fluent.Entities.Domain
{
public class Product : Entity<Product>
{
public virtual string ProductCode { get; set; } public virtual string ProductName { get; set; } public virtual string Description { get; set; } private readonly IList<Order> orders; public virtual IList<Order> Orders
{
get
{
return orders;
}
}
}
}
Order类
using System;
using System.Collections.Generic; namespace Demo.Fluent.Entities.Domain
{
public class Order : Entity<Order>
{
public virtual DateTime Ordered { get; set; }
public virtual DateTime? Shipped { get; set; }
public virtual Address ShipTo { get; set; }
public virtual Customer Customer { get; set; } private readonly IList<Product> products; public virtual IList<Product> Products
{
get
{
return products;
}
}
}
}
7)在Mapping文件夹下,定义映射类AddressMap、NameMap、CustomerMap、ProductMap和OrderMap。
类名称必须是值对象类型名称或实体类名称后面跟Map。
在映射类的无参构造函数内,调用Fluent NHibernate的API函数,定义映射。
AddressMap类
using Demo.Fluent.Entities.Domain;
using FluentNHibernate.Mapping; namespace Demo.Fluent.Entities.Mapping
{
public class AddressMap : ComponentMap<Address>
{
public AddressMap()
{
Map(x => x.Street).Length();
Map(x => x.City).Length();
Map(x => x.Province).Length();
Map(x => x.Country).Length();
}
}
}
NameMap类
using Demo.Fluent.Entities.Domain;
using FluentNHibernate.Mapping; namespace Demo.Fluent.Entities.Mapping
{
public class NameMap : ComponentMap<Name>
{
public NameMap()
{
Map(x => x.LastName).Not.Nullable().Length();
Map(x => x.FirstName).Not.Nullable().Length();
}
}
}
- Address类和Name类都时值对象类,因此他们的映射类文件都继承ComponetMap的泛型类。
- Map、Not.Nullable、Length都时Fluent NHibernate的API函数(见名知意),通过链式调用,对单个属性进行映射定义。
CustomerMap类
using Demo.Fluent.Entities.Domain;
using FluentNHibernate.Mapping; namespace Demo.Fluent.Entities.Mapping
{
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.ID).GeneratedBy.Native();
Component(x => x.Address);
Component(x => x.Name);
Map(x => x.Points);
Map(x => x.HasGoldStatus);
Map(x => x.MemberSince);
Map(x => x.CreditRating).CustomType<CustomerCreditRating>();
HasMany(x => x.Orders).Inverse().Cascade.AllDeleteOrphan().Fetch.Join();
}
}
}
- Customer类是实体类,继承ClassMap的泛型类。
- Id方法定义主键属性,调用Generate.Native()方法指出主键生成策略是indentity的。
- 对值对象类型的属性,调用Component方法,定义映射。
- HasMany方法生成OneToManyPart对象,映射一对多关系。
- HasMany方法调用后面的一串方法:Cascade.AllDeleteOrphan().Fetch.Join()对应了XML映射响应的属性。
关系映射的API方法:
一对一:HasOne
一对多:HasMany
多对对:HasManyToMany
ProductMap类
using Demo.Fluent.Entities.Domain;
using FluentNHibernate.Mapping; namespace Demo.Fluent.Entities.Mapping
{
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.ID).GeneratedBy.Native();
Map(x => x.ProductCode).Not.Nullable().Length();
Map(x => x.ProductName).Not.Nullable().Length();
Map(x => x.Description).Length();
HasManyToMany(x => x.Orders).Table("ProductOrder").ParentKeyColumn("ProductId").ChildKeyColumn("OrderId").Cascade.AllDeleteOrphan();
}
}
}
- HasManyToMany方法生成ManyToManyPart对象,映射Many-to-Many关系。
- Table("ProductOrder").ParentKeyColumn("ProductId").ChildKeyColumn("OrderId")
- 上面连串方法调用定义了Many-to-Many映射的中间表表名。对于Product表,这个关系的主键列和外键列。
- 多对多关系映射默认生成的中间表的名称是ProductToOrder。因此,这里要调用Table方法,用字符串定义中间表名称。
OrderMap类
using FluentNHibernate.Mapping; namespace Demo.Fluent.Entities.Domain
{
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Table("`Order`");
Id(x => x.ID).GeneratedBy.Native();
Map(x => x.Ordered);
Map(x => x.Shipped);
Component(x => x.ShipTo);
References(x => x.Customer).Column("CustomerId").Cascade.SaveUpdate();
HasManyToMany(x => x.Products).Table("ProductOrder").ParentKeyColumn("OrderId").ChildKeyColumn("ProductId").Cascade.All();
}
}
}
- Table方法定义映射的表名称,因为Order是SQL Server关键字,因此调用此方法,传入字符串"`Order`"作为表名称。生成的SQL语句的表名称字符串是"[Order]"。
- Many-to-One关系,实体类属性用Reference方法定义,指定外键列名称。
- 在ProductMap类构造函数内已经定义了中间表名称。因此,这里的Table("ProductOrder")可以省略。
8)添加用于测试的控制台应用程序Demo.Fluent.Console工程。
9)添加用于NHibernate设置的FluentConfig类。
using Demo.Fluent.Entities.Mapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate; namespace Demo.Fluent.Console
{
class FluentConfig
{
const string connString = "server=localhost;" + "database=NHibernateDemoDB;" + "integrated security=SSPI;"; public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connString))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>())
.BuildSessionFactory();
}
}
}
9)修改Program类。
using Demo.Fluent.Entities.Domain;
using NHibernate.Linq;
using System.Linq; namespace Demo.Fluent.Console
{
class Program
{
static void Main(string[] args)
{
var factory = FluentConfig.CreateSessionFactory();
using (var session = factory.OpenSession())
{
var customer = session.Get<Customer>();
System.Console.WriteLine("{0} {1}", customer.Name.LastName, customer.Name.FirstName); System.Console.WriteLine("order count: {0}",customer.Orders.Count()); System.Console.WriteLine();
System.Console.WriteLine("customers and their order count:");
var queryCount = session.Query<Customer>().Select(c => new
{
CustomerId = c.Id,
CustomerName = c.Name.FirstName + " " + c.Name.LastName,
Count = c.Orders.Count()
});
var listCount = queryCount.ToList();
if (listCount.Count > )
{
listCount.ForEach(o =>
{
System.Console.WriteLine("{0}-{1}: {2}", o.CustomerId, o.CustomerName, o.Count);
});
} System.Console.WriteLine(); System.Console.WriteLine("customers whose oders count greater than 2:");
var queryCountGreater = session.Query<Customer>().Where(c => c.Orders.Count > );
var listCountGreater = queryCountGreater.ToList();
if (listCountGreater.Count > )
{
listCountGreater.ForEach(o =>
{
System.Console.WriteLine("{0}-{1} {2}", o.Id, o.Name.FirstName, o.Name.LastName);
});
}
}
System.Console.WriteLine();
System.Console.WriteLine("Finished");
System.Console.ReadLine();
}
}
}
这里写了三个查询用来测试。第一个查询是通过Id查找Customer对象。第二个查询使用Linq to NHibernate对Customer和订单数量分组查询。第三个查询查找订单数大于2的Customer信息。
执行程序,得到结果(与数据库记录有关)。
结语
虽然Fluent NHibernate目前还不是很成熟(比起XML Mapping来说),但是绝大部分Mapping功能都已经可以能满足了。前面提过了他的优缺点,有兴趣的可以到Fluent NHibernate官网http://www.fluentnhibernate.org上去查看更详细的内容。
NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)的更多相关文章
- NHibernate系列文章二十八:NHibernate Mapping之Auto Mapping(附程序下载)
摘要 上一篇文章介绍了Fluent NHibernate基础知识.但是,Fluent NHibernate提供了一种更方便的Mapping方法称为Auto Mapping.只需在代码中定义一些Conv ...
- NHibernate系列文章二十二:NHibernate查询之HQL查询(附程序下载)
摘要 NHibernate提供了多种查询方式,最早的HQL语言查询.Criteria查询和SQL Query,到NHibernate 3.0的Linq NHibernate,NHIbernate 4. ...
- NHibernate系列文章二:创建NHibernate工程
摘要 这篇文章介绍了如何创建一个简单的使用NHibernate的控制台应用程序,包括使用NuGet.简单的配置.单表映射.对NHibernate配置文件添加智能提示.使用ISessionFactory ...
- NHibernate系列文章一:NHibernate介绍
摘要 NHibernate是一个成熟的开源的面向对象的.net映射框架.大量的实际项目中正在使用该框架.他是建立在ADO.Net基础之上.目前的版本是NHibernate 4.0.4.本系列文章都是基 ...
- NHibernate系列文章五:NHibernate配置
摘要 NHibernate有多种配置方法,代码,xml文件,以及Fluent NHibernate.这里只介绍最常用的两种NHibernate配置方法:通过代码和通过配置文件. 1. 通过代码配置 通 ...
- NHibernate系列文章七:NHibernate对象状态
摘要 NHibernate对象持久化 NHibernate对象的三个状态:临时态.持久态.游离态(托管态) NHibernate三状态的相互转化 1. NHibernate对象持久化 NHiberna ...
- NHibernate系列文章四:NHibernate运行时监控
摘要 有三种方式可以实现NHibernate运行时监控,监控的信息包括:执行了的SQL语句.NHibernate执行过程.数据库性能分析.这对我们学习NHibernate有很大的帮助,在工作中也能快速 ...
- NHibernate系列文章八:NHibernate对象一级缓存
摘要 Nhibernatea缓存非常强大,按照缓存存储在Session对象还是SessionFactory对象分为一级缓存和二级缓存. 一级缓存存在于Session对象里,也叫Session缓存,由S ...
- NHibernate系列文章二十五:NHibernate查询之Query Over查询(附程序下载)
摘要 这一篇文章介绍在NHibernate 3.2里引入的Query Over查询,Query Over查询跟Criteria查询类似.首先创建IQueryOver对象,然后通过调用该对象的API函数 ...
随机推荐
- 基于jQuery的email suggest插件
最近项目中有表单提交的地方需要用户填写邮箱,PM(产品经理)和运营都强烈要求在用户填写邮箱的时候出现suggest列表,简化用户输入的填写流程.我考虑了下,这个应该也是经常会用到的功能,细心的朋友可能 ...
- LINQ 联表查询 取count 值
linq to sql 实现左外部连接:var query=from a in A join b in B on a.ID equals b.aID into ab from a1 in ab.Def ...
- 压力测试报出503错误---ASP.NET支持大并发的相关配置
项目反馈报出503错误,需要收集性能数据如下: 1.Windows性能监视器,该应用程序池进程的线程和处理队列 2.问题重现时的进程dump 这是请求到达IIS后遇到的第一个队列,HTTP.sys收到 ...
- why happen "WaitHandles must be less than or equal to 64"
一.背景: 在一个项目中碰到大数据插入的问题,一次性插入20万条数据(SQL Server),并用200个线程去执行,计算需要花费多少时间,因此需要等200个线程处理完成后,记录花费的时间,需要考虑的 ...
- winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法
winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法 button的FlatAppearence属性下,设置BorderSize= ...
- linux下tomcat的安装和配置
安装前要求: 1. 安装java环境. 2. 配置java环境变量 开始了: 1. 在官网下载tomcat:http://tomcat.apache.org/ 2. linux环境选择.zip或者.t ...
- (OpenCV) VS2013 + opencv-2.4.10.exe + Windows 10 开发环境配置
主要配置2点: - Windows 环境变量. - VC++ 配置. STEP BY STEP: 1. 双击 ”opencv-2.4.10.exe“,解压到本地文件夹 “C:\ ". 2. ...
- Redis 简单命令
1. 新增 set keyName "keyValue" 2. 获取 get keyName 查看所有Key keys * 3. 删除 //删除当前数据库中的所有Key flush ...
- BestCoder Round #90 A.Kblack loves flag(随机数生成种子)
A.Kblack loves flag [题目链接]A.Kblack loves flag [题目类型]水题 &题意: kblack喜欢旗帜(flag),他的口袋里有无穷无尽的旗帜. 某天,k ...
- css3 flex盒子布局
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...