NHibernate系列文章二十八:NHibernate Mapping之Auto Mapping(附程序下载)
摘要
上一篇文章介绍了Fluent NHibernate基础知识。但是,Fluent NHibernate提供了一种更方便的Mapping方法称为Auto Mapping。只需在代码中定义一些Convention继承类,针对具体的属性、主键、关系、组件指定Mapping的规则,在实体类里定义简单的POCO对象就可以完成整个数据库的自动映射。Auto Mapping适合全新的系统开发,即是在系统设计时还没有数据库的时候。有点像Microsoft Entity Framework的Code First或是Model First的开发方式。
这篇文章介绍Fluent Mapping。本篇文章的代码可以到Fluent Auto Mapping下载。
1、Auto Mapping的优缺点
优点:
- 更少的Mapping代码,因为不需要大量显式定义映射关系。
- 数据库的Schema跟Model的定义更接近。
- 程序员可以把更多的精力放在特殊的映射关系上。因为定义的那些Convention已经帮你完成了大部分的工作了。
缺点:
- 因为大部分的映射都由Convention定义,不能方便地在细节上定义一些具体的映射关系。
- 对于已经存在的数据库系统,不太适合使用Auto Mapping。
2、程序演示
1)新建控制台应用程序工程Demo.Auto.Entities。
2)在新建的工程中,使用NuGet安装FluentNHibernate。
3)添加Enum、Domain文件夹和Mapping文件夹。
4)在Enum文件夹内添加文件Enums.cs。
- namespace Demo.Auto.Entities.Enum
- {
- public enum CustomerCreditRating
- {
- Excellent, VeryVeryGood, VeryGood, Good, Neutral, Poor, Terrible
- }
- }
5)在Domain文件夹内添加文件Entity.cs。
- namespace Demo.Auto.Entities.Domain
- {
- public abstract class Entity
- {
- public virtual int Id { get; private set; }
- }
- }
Entity类是所有实体类的基类,包含主键属性Id。
6)在Domain文件夹内添加Name.cs和Address.cs。
Name类
- using System;
- namespace Demo.Auto.Entities.Domain
- {
- public class Name
- {
- public string LastName { get; set; }
- public string FirstName { get; 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类
- namespace Demo.Auto.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类和Address类跟上一篇文章的Name类和Address类的代码一样,保持不变。
7)添加实体类Customer类、Product类和Order类。
Customer类
- using Demo.Auto.Entities.Enum;
- using System;
- using System.Collections.Generic;
- namespace Demo.Auto.Entities.Domain
- {
- public class Customer : Entity
- {
- 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; }
- public virtual IList<Order> Orders { get; set; }
- }
- }
Customer类继承Entity类,继承主键属性Id。
集合属性用IList接口定义。
Product类
- using System.Collections.Generic;
- namespace Demo.Auto.Entities.Domain
- {
- public class Product : Entity
- {
- public virtual string ProductCode { get; set; }
- public virtual string ProductName { get; set; }
- public virtual string Description { get; set; }
- public virtual IList<Order> Orders { get; set; }
- }
- }
Order类
- using System;
- using System.Collections.Generic;
- namespace Demo.Auto.Entities.Domain
- {
- public class Order : Entity
- {
- public virtual DateTime Ordered { get; set; }
- public virtual DateTime? Shipped { get; set; }
- public virtual Address ShipTo { get; set; }
- public virtual Customer Customer { get; set; }
- public virtual IList<Product> Products { get; set; }
- }
- }
8)在Mapping文件夹下添加文件AutoMappingConfiguration。
- using Demo.Auto.Entities.Domain;
- using FluentNHibernate.Automapping;
- using FluentNHibernate.Conventions;
- using FluentNHibernate.Conventions.Instances;
- using System;
- using FluentNHibernate;
- namespace Demo.Auto.Entities.Mapping
- {
- }
- 在namespace Demo.Auto.Entities.Mapping里添加DefaultAutomappingConfiguration的继承类AutoMappingConfiguration。设置哪些类型被映射成实体类,哪些类型被映射成组件类。
- public class AutoMappingConfiguration : DefaultAutomappingConfiguration
- {
- /// <summary>
- /// 类型是否是实体映射类型
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- public override bool ShouldMap(Type type)
- {
- //跟Customer类在一个名称空间的所有的类都被映射
- return type.Namespace == typeof(Customer).Namespace;
- }
- /// <summary>
- /// 类型是否是值对象映射类型
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- public override bool IsComponent(Type type)
- {
- //指定Address类和Name类是值对象映射类型
- return type == typeof(Address)
- || type == typeof(Name);
- }
- /// <summary>
- /// 映射值对象类型属性到数据库字段名
- /// </summary>
- /// <param name="member">值对象属性</param>
- /// <returns></returns>
- public override string GetComponentColumnPrefix(Member member)
- {
- //映射到数据库列名的前缀为空。默认生成的组件列列名是类名+属性名。例:CustomerCity
- return "";
- }
- }
- 添加IIdConvention接口的继承类IdConvention。指定主键列列名、主键生成策略。
- public class IdConvention : IIdConvention
- {
- public void Apply(IIdentityInstance instance)
- {
- instance.GeneratedBy.Native();
- }
- }
这里指定所有的主键列的生成策略是Native的。默认的主键列名称是Id。
- 添加IPropertyConvention接口的继承类DefaultStringLengthConvention。指定一般属性的通用映射规则。
- public class DefaultStringLengthConvention : IPropertyConvention
- {
- public void Apply(IPropertyInstance instance)
- {
- instance.Length();
- }
- }
这里指定所有string类型属性的长度是250个字符。默认是255。
- 添加IHasManyToManyConvention接口的继承类HasManyToManyConvention。指定Many-to-Many映射的一般规则。
- public class HasManyToManyConvention : IHasManyToManyConvention
- {
- public void Apply(IManyToManyCollectionInstance instance)
- {
- //指定主键列列名是属性名+Id,例:ProductId
- instance.Key.Column(instance.EntityType.Name + "Id");
- //指定外键列列名是属性名+Id,例:OrderId
- instance.Relationship.Column(instance.Relationship.StringIdentifierForModel + "Id");
- var firstName = instance.EntityType.Name; //主表映射类属性名
- var secondName = instance.ChildType.Name; //从表映射类属性名
- //定义关系的中间表表名。按主表和从表属性名的字母顺序设置中间表表名。
- //例:Product和Order,按字母顺序,字符串"Product"在"Order"之前,中间表表名设置为"ProductOrder"。
- //控制反转只设置成只有一个方向。
- if (StringComparer.OrdinalIgnoreCase.Compare(firstName, secondName) > )
- {
- instance.Table(string.Format("{0}{1}", firstName, secondName));
- instance.Not.Inverse(); //不反转
- }
- else
- {
- instance.Table(string.Format("{0}{1}", secondName, firstName));
- instance.Inverse(); //反转
- }
- //级联更新Casade,两个方向都设置成All
- instance.Cascade.All();
- }
- }
详细说明见代码中注释。
- 添加IHasManyConvention接口的继承类HasOneToManyConvention。指定One-to-Many的一般映射规则。
- public class HasOneToManyConvention : IHasManyConvention
- {
- public void Apply(IOneToManyCollectionInstance instance)
- {
- //指定从表的外键列列名是属性名+Id,例:CustomerId
- instance.OtherSide.Column(instance.OtherSide.Name + "Id");
- //级联更新Casade:主表到从表设置成All
- instance.Cascade.All();
- }
- }
9)在Mapping文件夹下添加文件MappingOverride.cs。在这个文件里添加一些继承IAutoMappingOverride接口的类,可以对具体的一些实体类的映射进行重写。
- using Demo.Auto.Entities.Domain;
- using FluentNHibernate.Automapping;
- using FluentNHibernate.Automapping.Alterations;
- namespace Demo.Auto.Entities.Mapping
- {
- }
在namespace Demo.Auto.Entities.Mapping下添加三个类:CustomerMappingOverride、ProductMappingOverride、OrderMappingOverride。分别对实体类Customer、Product、Order的映射进行部分重写。
- public class CustomerMappingOverride : IAutoMappingOverride<Customer>
- {
- public void Override(AutoMapping<Customer> mapping)
- {
- mapping.Map(x => x.CreditRating).CustomType<Enum.CustomerCreditRating>();
- mapping.HasMany(x => x.Orders).Inverse().Cascade.AllDeleteOrphan().Fetch.Join();
- }
- }
- public class ProductMappingOverride : IAutoMappingOverride<Product>
- {
- public void Override(AutoMapping<Product> mapping)
- {
- mapping.Map(x => x.ProductCode).Not.Nullable().Length();
- mapping.Map(x => x.ProductName).Not.Nullable().Length();
- mapping.HasManyToMany(x => x.Orders).Cascade.AllDeleteOrphan();
- }
- }
- public class OrderMappingOverride : IAutoMappingOverride<Order>
- {
- public void Override(AutoMapping<Order> mapping)
- {
- mapping.References(x => x.Customer).Cascade.SaveUpdate();
- }
- }
10)修改Main函数,测试Auto Mapping。
- using Demo.Auto.Entities.Domain;
- using Demo.Auto.Entities.Mapping;
- using FluentNHibernate.Automapping;
- using FluentNHibernate.Cfg;
- using FluentNHibernate.Cfg.Db;
- using NHibernate.Tool.hbm2ddl;
- using System;
- namespace Demo.Auto.Entities
- {
- class Program
- {
- const string connString = "server=localhost;" + "database=NHibernateDemoDB;" + "integrated security=SSPI;";
- static void Main(string[] args)
- {
- var cfg = new AutoMappingConfiguration();
- var configuration = Fluently.Configure()
- .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connString))
- .Mappings(m =>
- m.AutoMappings.Add(AutoMap.AssemblyOf<Customer>(cfg)
- .Conventions.Setup(c =>
- {
- c.Add<IdConvention>();
- c.Add<DefaultStringLengthConvention>();
- c.Add<HasOneToManyConvention>();
- c.Add<HasManyToManyConvention>();
- })
- .UseOverridesFromAssemblyOf<CustomerMappingOverride>()
- .UseOverridesFromAssemblyOf<ProductMappingOverride>()
- .UseOverridesFromAssemblyOf<OrderMappingOverride>()
- ).ExportTo(@"c:\daniel"))
- .BuildConfiguration();
- var exporter = new SchemaExport(configuration);
- exporter.Execute(true, false, false);
- Console.Write("Hit enter to exit:");
- Console.ReadLine();
- }
- }
- }
- FluentConfiguration对象的Mapping方法传入Lamda表达式指定Mapping方式。
- AutoMap.AssemblyOf<Customer>(cfg):指定使用自动映射,传入使用自定义类AutoMappingConfiguration的对象cfg,按自定义类AutoMappingConfiguration中的重载方法进行映射。方法调用生成AutoPersistenceModel对象。
- AutoPersistenceModel对象的Conventions.Setup方法传入Lamda表达式,添加一系列的Convention。
- AutoPersistenceModel对象的UseOverridesFromAssemblyOf方法,传入继承于IAutoMappingOverride接口的类作为泛型参数,添加一系列的Override。
- ExportTo(@"c:\daniel"))方法将自动映射的定义xml文件导出到文件夹c:\daniel。
- var exporter = new SchemaExport(configuration);
- exporter.Execute(true, false, false);
这两行代码生成创建数据库表的SQL语句。SchemaExport对象的Execute方法传入三个bool类型参数。第一个参数表示是否将SQL语句显示到控制台,第二个参数表示是否立即执行SQL语句,第三个参数表示是否删除并重建数据库表。
在C盘下创建文件夹daniel,执行程序,得到控制台输出:
到C:\daniel文件夹下,看到生成的三个xml配置文件。
打开这三个文件,看到跟手写的映射文件是一样的。
结语
Fluent NHibernate提供的Auto Mapping确实是一个很方便的方式,大量地减少了手写映射的代码量。对于新的项目的确是一个不错的映射方式。有兴趣的可以到Fluent NHibernate官网http://www.fluentnhibernate.org上去查看更详细的内容。
NHibernate系列文章二十八:NHibernate Mapping之Auto Mapping(附程序下载)的更多相关文章
- NHibernate系列文章二十:NHibernate关系之一对一(附程序下载)
摘要 NHibernate一对一关系虽然不经常碰到,但是在对于数据库结构优化的时候,经常会碰到一对一关系.比如,产品详细信息比较多的时候,可以把产品详细信息放到另一张表里面,Product主表只记录产 ...
- WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]
原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...
- NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)
摘要 从这一节起,介绍NHibernate Mapping的内容.前面文章都是使用的NHibernate XML Mapping.NHibernate XML Mapping是NHibernate最早 ...
- NHibernate系列文章二十四:NHibernate查询之Linq查询(附程序下载)
摘要 NHibernate从3.0开始支持Linq查询.写Linq to NHibernate查询就跟写.net linq代码一样,非常灵活,可以很容易实现复杂的查询.这篇文章使用Linq to NH ...
- NHibernate系列文章二十五:NHibernate查询之Query Over查询(附程序下载)
摘要 这一篇文章介绍在NHibernate 3.2里引入的Query Over查询,Query Over查询跟Criteria查询类似.首先创建IQueryOver对象,然后通过调用该对象的API函数 ...
- NHibernate系列文章二十二:NHibernate查询之HQL查询(附程序下载)
摘要 NHibernate提供了多种查询方式,最早的HQL语言查询.Criteria查询和SQL Query,到NHibernate 3.0的Linq NHibernate,NHIbernate 4. ...
- NHibernate系列文章二十六:NHibernate查询之SQL Query查询(附程序下载)
摘要 NHibernate在很早的版本就提供了SQL Query(原生SQL查询),对于很复杂的查询,如果使用其他的查询方式实现比较困难的时候,一般使用SQL Query.使用SQL Query是基于 ...
- NHibernate系列文章二十三:NHibernate查询之Criteria查询(附程序下载)
摘要 上一篇文章介绍了NHibernate HQL,他的缺点是不能够在编译时发现问题.如果数据库表结构有改动引起了实体关系映射的类有改动,要同时修改这些HQL字符串.这篇文章介绍NHibernate面 ...
- NHibernate系列文章二:创建NHibernate工程
摘要 这篇文章介绍了如何创建一个简单的使用NHibernate的控制台应用程序,包括使用NuGet.简单的配置.单表映射.对NHibernate配置文件添加智能提示.使用ISessionFactory ...
随机推荐
- 推荐的Android ORM框架
1. OrmLite OrmLite 不是 Android 平台专用的ORM框架,它是Java ORM.支持JDBC连接,Spring以及Android平台.语法中广泛使用了注解(Annotation ...
- Java Junit单元测试
使用Junit进行单元测试,首先引入Junit的jar,配置如下. @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(&quo ...
- ILGenerator.Emit动态 MSIL编程(三)之动态代理
using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; public seale ...
- YII rules常见规则
public function rules() { return array( //必须填写 array('email, username, password, ...
- vmware12无法打开内核设备“\\.\Global\vmx86”
vmware12 无法打开内核设备"\\.\Global\vmx86": 系统找不到指定的文件.你想要在安装 VMware Workstation 前重启吗? 打开vmware12 ...
- Objective-C的 KVC和KVO
字面意思分别是: KVC是指key value coding,键值编码. KVO是指key value observing,键值观察. 直白的说法是: KVC就是将一个对象的属性及其值当做一个字典,可 ...
- WAMP虚拟目录的设置
1.打开Apache的配置文件httpd.conf,并去掉#Include conf/extra/httpd-vhosts.conf前面的#!! 2.打开Apache的apache/conf/extr ...
- 动画--问题追踪:ImageView执行缩放动画ScaleAnimation之后,图像显示不全的问题。
http://www.bkjia.com/Androidjc/929473.html: 问题追踪:ImageView执行缩放动画ScaleAnimation之后,图像显示不全的问题., 问题:我有一个 ...
- Python 多线程
一.线程的使用 需导入模块: from threading import Thread 二.基本使用 def fun1(arg1, v): print(arg1) print('before') t1 ...
- JAVA下载文件中文乱码问题
http://blog.itpub.net/92037/viewspace-788900/ 最后的中文乱码没有解决 现在我在系统中用到了两个组件,smartupload,一个支持中文,一个不支持.但是 ...