博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Fluent NHibernate Component
阅读量:5139 次
发布时间:2019-06-13

本文共 14258 字,大约阅读时间需要 47 分钟。

NHibernate is great, (no doubt about it) but every time I was supposed to work with it, the fact that I had to manually type in all those xml configuration entries was something I really didn’t like at all.

The most important complains I personally have on NHibernate configuration files are:

  • It is error prone
  • Errors in configuration file can be hard to trace
  • It is not refactoring friendly
  • It is not C#, it is XML :-)

I believe that the most important reason why NHibernate is not more widely adopted by general DEV population is exactly the “Java XML configuration” PIA feeling you have while working with it.

Great news for all of us from that group: NHibernate is possible to be used WITHOUT configuration files!

 

Who, what and where?

Jeremy Miller coded initial bits of the Fluent NHibernate project and then with couple of guys more bring the project to Google code and they started finalizing it. OSS rules!

So, if you go to you would see names of other project members together with the simple code illustrating the goal fluent nhibernate project tries to achieve.

As with the most OSS projects, to get the source code of FNH, download it from Subversion trunk located at http://fluent-nhibernate.googlecode.com/svn/trunk/

 

Solution file consist of couple of projects:

  • LIb solution folder contains DLL you would be using in your application.
  • Test project containing a bunch of unit tests which you can use as starting point to get detail understanding on how the things works.
  • Quick start project which highlights most important concepts you need to start using NHibernate

Note: Project is initially built with NHibernate 2.0 which collides with (using NHibernate 2.1) so I updated FNH project references to use 2.1 too 

Fluent NHibernate in action

Solution project structure

In this blog post, I will use the same example used in his NHibernate chapter so you can compare outcome of my blog post sample it with how it looked in his book (with the configuration files in place).

Solution file of today’s example contains 3 projects:

  • BusinessLayer (“Domain”)
    Contains definitions of entities which we use in modeling business use cases.
    Important to be mentioned here is that BusinessLayer  project doesn’t have any reference to neither other projects of this solution nor the “3pty dlls” such is NHibernate.dll
  • NHConfigMappings (“Mapping project”)
    Contains ORM mapping definitions which enables nhibernate to persist to relational DB domain entities
  • NHCongigMappings.Test

Contains test fixture illustrating how mappings should be consumed and how nhibernate engine could be initialized without any xml configuration files

(Source code used in today blog post can be download from ) 

 

Domain project

 

 

 

As you can see, it is very simple sample where we have entity customer class containing “embedded” Address value object and referencing the list of reference persons customer contacted.

Same diagram given in code would look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using
System;
using
System.Collections.Generic;
  
namespace
CustomerConfiguration
{
    
public
class
Customer
    
{
        
public
virtual
Guid Id {
get
;
set
; }
  
        
public
virtual
string
CustomerNumber {
get
;
set
; }
  
        
public
virtual
string
Name {
get
;
set
; }
  
        
public
virtual
Address CustomerAddress {
get
;
set
; }
  
        
public
virtual
IList<REFERENCEPERSON> ReferencePersons {
get
;
set
; }
    
}
}

Address type will look like this:

1
2
3
4
5
6
7
8
9
10
namespace
CustomerConfiguration
{
    
public
class
Address
    
{
        
public
virtual
string
Street {
get
;
set
; }
        
public
virtual
string
PostalCode {
get
;
set
; }
        
public
virtual
string
Town {
get
;
set
; }
        
public
virtual
string
Country {
get
;
set
; }
    
}
}

And ReferencePerson type would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
using
System;
  
namespace
CustomerConfiguration
{
    
public
class
ReferencePerson
    
{
        
public
virtual
Guid Id {
get
;
set
; }
  
        
public
virtual
string
FirstName {
get
;
set
; }
  
        
public
virtual
string
SurName {
get
;
set
; }
    
}
}

As you can see from the code above, NHibernate allows us fully software development: no special attributes, no special base class or interface…

 

 

Mapping project

Once we have domain logic defined, we need to provide information on how NHibernate should map our classes with appropriate DB entities: tables and columns.

For my example DB would look like this:

As you can see, although in my code I have separate Address class I don’t have it in DB. Instead I have it embedded in Customer table as last 4 columns. Also, table names in DB are in plural form while names of classes are in singular form.

Another small difference here is that in ReferencePerson class has a LastName property while DB has column SurName.

Mapping ReferencePerson

I won’t throw in here example of how that would look if I would use XML configuration file, because the whole point is to forget that ASAP :) (In case you want to see it, check it out )

In order to define NHibernate mapping, in FNH you need to create a mapping class for each one of entities (which is something ).

That mapping class should inherit from  ClassMap<T> class in order to get access to FHN members providing programmatic access over configuration settings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using
CustomerConfiguration;
using
FluentNHibernate.Mapping;
  
namespace
NHConfigMappings
{
    
public
class
ReferencePersonMap : ClassMap<ReferencePerson>
    
{
        
public
ReferencePersonMap()
        
{
            
TableName = “ReferencePersons”;
              
            
Id(x => x.Id).GeneratedBy.GuidComb()
                
.WithUnsavedValue(“00000000-0000-0000-0000-000000000000”);
  
            
Map(x => x.FirstName).WithLengthOf(50).CanNotBeNull();
            
Map(x => x.LastName,”SurName”).WithLengthOf(50).CanNotBeNull();
        
}
    
}
}

Main advantage of is that  it produces code with good readability, which I believe we can see from code snippet above.

In a class constructor, I am defining:

  1. What is the name of data table where the class will be persisted. In my example table name and class name differs so I had to define it here, but I think it is good practice to define it always, even they are implicitly the same by the default
  2. Which class property should be used as populating table PK. In example I took I am using ReferenceMap.Id property where NHibernate generate its value using   value as performance effective type of GUID values. At the end I defined how unsaved value is looking like so NHibernate would be able to deduct how the class should be persisted (insert or update)
  3. That the FirstName property should be mapped to the column with the same name where the column length is 50 characters and null values are not allowed
  4. That the LastName property should be mapped to the column named “SurName” with the maximum length of 50 characters without null values being allowed

In my opinion main advantages here are that the configuration code is human readable and refactoring friendly (if I ever do changes on ReferencePerson class mapping file will be broken so we have “type safe” configuration settings)

Mapping Customer

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Customer class mapping definition is stored in CustomerMap (implementing the ClassMap<Customer> class looking something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using
CustomerConfiguration;
using
FluentNHibernate.Mapping;
  
namespace
NHConfigMappings
{
    
public
class
CustomerMap : ClassMap<Customer>
    
{
        
public
CustomerMap()
        
{
            
TableName = “Customers”;
              
            
Id(x => x.Id).GeneratedBy.GuidComb()
                
.WithUnsavedValue”00000000-0000-0000-0000-000000000000”);
              
            
Map(x => x.Name).WithLengthOf(50);
            
Map(x => x.CustomerNumber).WithLengthOf(50);
              
        
HasMany<ReferencePerson>(x => x.ReferencePersons)
                
.Access.AsProperty()
                
.AsBag().WithKeyColumn(
"CustomerId"
)
                
.Cascade.All();                        
              
            
Component<Address>(x => x.CustomerAddress, 
                               
m =>
                                   
{
                                       
m.Map(x => x.Street).WithLengthOf(100);
                                       
m.Map(x => x.PostalCode).WithLengthOf(6);
                                       
m.Map(x => x.Town).WithLengthOf(30);
                                       
m.Map(x => x.Country).WithLengthOf(50);
                                   
});
        
}
    
}
}

In a class constructor, I am defining:

  1. The name of table to which this class map
  2. Customer.Id as primary key containing guid value
  3. Customer.Name is mapped to the Name nvarchar(50) column
  4. Customer.CustomerNumber is mapped to the CustomerNumber nvarchar(50) column
  5. Customer class contains collection of ReferencePerson instances stored in Customer.ReferencePersons property
    which should be mapped in separate table (AsBag())  where role of FK will be performed by ReferencePerson.CustomerId

     

    and with cascading of events (Insert of Customer will result with Insert of ReferencePerson etc)

  6. Customer contains property CustomerAddress of type Address as embed value where
    1. Address.Street is nvarchar(100) column
    2. Address.PostalCode  is nvarchar(6) column
    3. Address.Town is nvarchar(30) column
    4. Address.Country is nvarchar(50) column

Once again, take a look at the class above. I have just described something which looks like typical use case we face in real world with something which (IMHO) has very high readability and  it is easy to be written

 

 

Unit testing fluent nhibernate mappings

In order to present how those mappings should be used I wrote unit test project which will present:

  • how this mappings are to be used
  • how to configure nhibernate itself without the need for hibernate.cfg.xml file

For the purpose of this test I choose MS Test just for kicks (although I am mostly using MBUnit )

 

How to initialize nhibernate without hibernate.cfg.xml

Due to the fact that initializing nhibernate engine is slow, I am initializing it once per text fixture and in test fixture of this example nhibernate initialization routine in case of fluent nhibernate is a 5 step procedure:

  • Initialize type implementing IPersistenceConfigurer (MSSqlConfiguration, SQLLiteConfiguration, PostgreSQLConfiguration are currently supported)
  • use instance of that type (in my example instance of MSSqlConfiguration) to preset properties of NHibernate.Configuration instance
  • create instance of FluentHibernate.PersistenceModel type
  • use that instance to load the assembly containing mappings
  • use that instance to inject mappings to nhibernate configuration instance

At the end of configuration sequence I have  operational NHibernate configuration instance which I use to create session factory which I then store as a class field.

Once I have that SessionFactory field in place, each one of the test methods just uses it to create a session needed.

Let’s take a look at how this initialization routine could look in code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private
static
ISessionFactory _sessionFactory;
 
    
[ClassInitialize]
    
public
static
void
FixtureInit(TestContext testContext)
    
{
        
// initialize persistance configurer
        
IPersistenceConfigurer persistenceConfigurer =
            
MsSqlConfiguration
                
.MsSql2005
                
.ConnectionString.Is(
                
“Data Source=.SQL2008;Initial Catalog=NHibernateBlog;"
        
+ "Integrated Security=True”)
                
.ShowSql();
 
        
// initialize nhibernate with persistance configurer properties
        
Configuration cfg = persistenceConfigurer
            
.ConfigureProperties(
new
Configuration());
 
        
// add mappings definition to nhibernate configuration
        
var persistenceModel =
new
PersistenceModel();
        
persistenceModel.addMappingsFromAssembly
        
(Assembly.Load(“NHConfigMappings”));
        
persistenceModel.Configure(cfg);
 
        
// set session factory field which is to be used in tests
        
_sessionFactory = cfg.BuildSessionFactory();
    
}

I think code above is simple enough (thanks to FNH) that it doesn’t need any extra explanations about its  concrete implementation.

Nhibernate unit test method

Now when we have that session factory up and going, we would make only a simple test where we will create a new customer and try to store it in DB.

That test code could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[TestMethod]
public
void
ReferencePerson_Create_ShouldCreateRowInDb()
{
    
var customer =
new
Customer
       
{
           
Name = “John Doe”,
           
CustomerNumber = “12345”,
           
CustomerAddress =
new
Address
                                 
{
                                     
Street = “1st Mayson Street”,
                                     
PostalCode = “01754”,
                                     
Town = “Maynard”,
                                     
Country = “USA”
                                 
},
           
ReferencePersons =
new
List<ReferencePerson>
                                  
{
                                      
new
ReferencePerson
                                          
{
                                              
FirstName = “Nikola”,
                                              
LastName = “Malovic”
                                          
}
                                  
}
       
};
 
    
ISession session = _sessionFactory.OpenSession();
    
Guid id=(Guid)session.Save(customer);
    
session.Flush();
 
    
session.Evict(customer);
      
    
var customerDB= session.Get<Customer>(id);
    
Assert.IsTrue(  customerDB.Id == customer.Id && customerDB.Name == customer.Name 
                    
&& customerDB.CustomerAddress.Street == “1st Mayson Street”
                    
&& customerDB.ReferencePersons[0].FirstName == “Nikola”);
 
}

At the beginning of the test method I create a new customer instance with CustomerAddress data and single ReferencePerson instance in ReferencePersons.

Then I use the sessionFactory created in test fixture set up, and create a session which then I use to persist customer instance. As a result of that persistence method call I am getting customer identity value used as PK value in DB.At the end of that persistence code I just flush the session in order to commit the changes.

Now when (hopefully) our customer is saved I need to test that and the usual way of testing save is to try to load the data from DB. That’s why I remove the customer instance from NHibernate Identity Map “cache” so I won’t get results from memory and then I am trying to retrieve data using the identity value I retrieved during persistence.

With the retrieved customer data I do just quick value check (in real world test this check would look different).

Test will pass therefore if retrieved data will match the data of customer instance created in this test.

 

Running the test

Once I run the test, I got successful test 

And fast look at the DB shows that data are persisted

Test passed!

 

 

Conclusion

Purpose of this blog post was to present a way of using NHibernate different then the standard xml configuration approach most of people use right now working with NHibernate.

I hope you can see for yourself that it is VERY possible to use NHibernate without a single configuration file on very DEV friendly and intuitive way.

The example I presented here is just tip of the iceberg so I strongly encourage you to sync Fluent NHibernate source code and play with quick start and test projects.

Here are also some links you can check out too:

Happy Hibernating!

转载于:https://www.cnblogs.com/aaa6818162/archive/2013/05/09/3069135.html

你可能感兴趣的文章
机器学习之路:python 集成分类器 随机森林分类RandomForestClassifier 梯度提升决策树分类GradientBoostingClassifier 预测泰坦尼克号幸存者...
查看>>
AJAX 跨源 HTTP 请求
查看>>
Web 项目系列之浏览器机制(一)
查看>>
IOS真机调试
查看>>
IOS正则表达式的用法简介
查看>>
day31 python学习 并发编程之多进程理论部分
查看>>
Uploadify jquery+flash+UploadHandler.ashx
查看>>
Java-P:对象创建
查看>>
IIS 配置
查看>>
软件人机界面设计
查看>>
requests模块下载视频 显示进度和网速
查看>>
Elasticsearch 学习之 分片未分配原因
查看>>
.NET项目从CI到CD-Jenkins_Pipeline的应用
查看>>
E2.(b)
查看>>
ini 解析库,config 解析库不完全列表
查看>>
awk常见操作整理(更新)
查看>>
javaEE项目实践——学生信息管理系统
查看>>
E. Black Box
查看>>
( ̄▽ ̄") 没钱了
查看>>
模拟退火算法实现代码
查看>>