代码改变世界

Fluent NHibernate 配置篇

2013-03-06 18:06  午夜瞎想  阅读(5617)  评论(2编辑  收藏  举报

前言:

       以下内容Fluent Nhibernate官方wiki里面都有,并且都是基本的用法,只是对于刚刚使用时遇到的问题点做一下整理,希望可以对那些不喜欢看官方help的同学有所帮助:)

目录:

  1. 数据库配置

  2. FluentMappings和AutoMappings混合使用

  3. 枚举类型配置

  4. 生成sql日志配置

  5. AutoMap时不允许Nhibernate自动生成Id

  6. AutoMap时配置Image字段长度

数据库配置

1 private static ISessionFactory CreateSessionFactory()
2 {
3   return Fluently.Configure()
4    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
5     
6 }

FluentMappings和AutoMappings混合使用

Fluent NHibernate的AutoMapping功能还是极大的提高开发效率的,只要满足基本的默认规则就行,在建立新项目时尤其好用。但在需要使用已存在的表或者是不满足默认规则的时候就可能需要自动以Map来实现了。配置代码如下:

 1 public static FluentConfiguration GetConfiguration()
 2         {
 3             var model = AutoMap.Assembly(Assembly.Load("EntApp.ShinFlow.Data")).
 4             Where(t => t.Namespace == "EntApp.ShinFlow.Data.Model")
 5             .IgnoreBase<BaseEntity>()
 6             ;
 7 
 8             return Fluently.Configure()
 9                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
10                 .Mappings(m=>{
11                     m.AutoMappings.Add(model);
12                     m.FluentMappings.AddFromAssemblyOf<EntApp.ShinFlow.Data.CustomModel.User>();
13                 });
14                 15         }

其中第3行是加载自动Map的Assembly,第4行为只有名称空间为EntApp.ShinFlow.Data.Model的Model才自动Map。

第12行是常规的FluentMapping,我这里使用名称空间来区分哪些实体类可以进行AotoMap,FluentNibernate还提供了其他条件,比如type,basetype等等属性。

枚举类型配置

这个功能是我最喜欢的,因为以前用Entity framework时没办法支持,每次都要变相的使用,现在Nhibernate可方便的使用。

默认条件下Nhibernate就支持枚举类型,但会取枚举的.ToString进行存储,往往在数据库设计的时候希望存储枚举对应的int值,这就需要重写Nhibernate的IUserTypeConvention接口来实现

 1 public class EnumConvention : IUserTypeConvention
 2     {
 3         public void Apply(IPropertyInstance instance)
 4         {
 5             instance.CustomType(instance.Property.PropertyType);
 6         }
 7 
 8         public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
 9         {
10             criteria.Expect(x => x.Property.PropertyType.IsEnum);
11         }
12         public bool Accept(Type type)
13         {
14             return type.IsEnum;
15         }
16     }

然后在FluentConfiguration环节配置上。

 1  public static FluentConfiguration GetConfiguration()
 2         {
 3             var model = AutoMap.Assembly(Assembly.Load("EntApp.ShinFlow.Data")).
 4             Where(t => t.Namespace == "EntApp.ShinFlow.Data.Model")
 5             .IgnoreBase<BaseEntity>()
 6             .Conventions.AddFromAssemblyOf<EnumConvention>();
 7 
 8             return Fluently.Configure()
 9                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
10                 .Mappings(m=>{
11                     m.AutoMappings.Add(model);
12                     m.FluentMappings.AddFromAssemblyOf<EntApp.ShinFlow.Data.CustomModel.User>();
13                 });
14         }

在如下实体类保存到数据库的就是SampleCharEnum对应的1、2、3

 1 public virtual SampleCharEnum Type
 2         {
 3             get;
 4             set;
 5         }
 6 
 7 public enum SampleCharEnum
 8     {
 9         On = 1,
10         Off = 2,
11         Dimmed = 3
12     }

生成sql日志配置

这个功能也是我比较喜欢Nhibernate的地方,因为Entity Framework依然没有类似的功能,每次调试都需要用sql profiler来进行协助。

只需要继承EmptyInterceptor类,并在Config里面配置一下就ok了。

1 public class SqlStatementInterceptor : EmptyInterceptor
2     {
3         public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
4         {
5             System.Diagnostics.Trace.WriteLine(sql.ToString());
6             return sql;
7         }
8     }
 1 public static FluentConfiguration GetConfiguration()
 2         {
 3             var model = AutoMap.Assembly(Assembly.Load("EntApp.ShinFlow.Data")).
 4             Where(t => t.Namespace == "EntApp.ShinFlow.Data.Model")
 5             .IgnoreBase<BaseEntity>()
 6             .Conventions.AddFromAssemblyOf<EnumConvention>();
 7 
 8             return Fluently.Configure()
 9                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
10                 .Mappings(m=>{
11                     m.AutoMappings.Add(model);
12                     m.FluentMappings.AddFromAssemblyOf<EntApp.ShinFlow.Data.CustomModel.User>();
13                 })
14                 .ExposeConfiguration(f=>f.SetInterceptor(new SqlStatementInterceptor()));
15         }

AutoMap时不允许Nhibernate自动生成Id

用EF时主键ID都是自己new的guid,在做主从表的时候很方便,但fluent nhibernate默认是自动给生成主键ID的,这里我们通过统一的配置来修改这一默认设置

1 public class AssignedIdConvention : IIdConvention
2     {
3         public void Apply(IIdentityInstance instance)
4         {
5             instance.GeneratedBy.Assigned();
6         }
7     }

实现一个IConvention接口,告诉系统主键生成规则。这里主键生成规则Nhibernate内置有几种,请自行google。

然后加到配置里面

 1 public static FluentConfiguration GetConfiguration()
 2         {
 3             var model = AutoMap.Assembly(Assembly.Load("EntApp.ShineFlow.Data")).
 4             Where(t => t.Namespace == "EntApp.ShineFlow.Data.Model")
 5             .IgnoreBase<BaseEntity>()
 6             .Conventions.AddFromAssemblyOf<EnumConvention>()
 7             .Conventions.Add<AssignedIdConvention>()
 8             .Conventions.Add < BinaryColumnLengthConvention>()
 9             ;
10 
11             return Fluently.Configure()
12                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
13                 .Mappings(m=>{
14                     m.AutoMappings.Add(model);
15                     m.FluentMappings.AddFromAssemblyOf<EntApp.ShineFlow.Data.CustomModel.User>();
16                 })
17                 
18                 .ExposeConfiguration(f=>f.SetInterceptor(new SqlStatementInterceptor()));
19         }

注意第7行。

AutoMap时配置Image字段长度

做附件上传时,需要把附件存入数据库的Image字段,相对应的实体类的类型为byte[],但nhibernate默认长度只能存8k,我们需要把限制改一下。

 1  public class BinaryColumnLengthConvention : IPropertyConvention, IPropertyConventionAcceptance
 2     {
 3         public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
 4         {
 5             criteria.Expect(x => x.Property.PropertyType == typeof(byte[]));
 6         }
 7 
 8         public void Apply(IPropertyInstance instance)
 9         {
10             instance.Length(2147483647);
11             instance.CustomSqlType("varbinary(MAX)");
12         }
13     }

这里我们通过继承两个接口,如果字段类型为byte[]时,把长度设置的最长。然后加入系统配置。

 1  public static FluentConfiguration GetConfiguration()
 2         {
 3             var model = AutoMap.Assembly(Assembly.Load("EntApp.ShineFlow.Data")).
 4             Where(t => t.Namespace == "EntApp.ShineFlow.Data.Model")
 5             .IgnoreBase<BaseEntity>()
 6             .Conventions.AddFromAssemblyOf<EnumConvention>()
 7             .Conventions.Add<AssignedIdConvention>()
 8             .Conventions.Add < BinaryColumnLengthConvention>()
 9             ;
10 
11             return Fluently.Configure()
12                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
13                 .Mappings(m=>{
14                     m.AutoMappings.Add(model);
15                     m.FluentMappings.AddFromAssemblyOf<EntApp.ShineFlow.Data.CustomModel.User>();
16                 })
17                 
18                 .ExposeConfiguration(f=>f.SetInterceptor(new SqlStatementInterceptor()));
19         }

注意第8行

结尾:

         从Entity framework的Code first刚出来时就非常关注,在项目中使用的也比较多,毕竟是微软的亲生儿子。但最近在刚刚使用Nhibernate时就发现了一些Entity所不具备的优点,比如枚举的支持,sql日志的输出,存储过程的支持,sql的混用,xml 和automap的混用等等,明显发现比entity framwork更灵活方便。

        当然最新版的Entity Framework也增加了许多功能,但总体感觉还是太慢,希望EF加油吧!