O/R Mapping 研究报告(5) --- 使用NHibernate进行开发--Vs 2005 C# (转自 飞鹰手记)
Posted on 2007-05-25 19:06 落花人独立 阅读(2123) 评论(10) 编辑 收藏 举报使用NHibernate开发主要有下面的四个步骤:
① 创建实体层的类(Cool Coder工具生成)
1,在这步就用CodeSmith来生成吧,觉得Cool Code生成的数据类型都是原来长度的一半,另外主要的原因是我用cool code生成的代码没有成功,当然大部分是我的原因。随着对其了解的深入,一些问题也得到了解决。但是,到了解决的时候我就用的是CodeSmith工具了。
***************************************************
2,用这些自动代码生成工具的时候,生成的代码还是要手工去改的,不是生成了就完事大吉了。还是需要我们去了解NHibernate的映射原理,知道哪部分是做什么的,这样改的时候才能够得心应手。主要的参考文献就是NHibernate的帮助和NHibernate的官方网站。查文档的时候第一个要从这里查起。
*************************************
3,CodeSmith在NHibernate开发中的用法参见http://www.cnblogs.com/abluedog/archive/2006/04/15/375862.aspx。在这里已经介绍的很详细了,所以我就不多说了。在做这个工作之前是要读好的。
****************************************
4,在用CodeSmith生成代码的时候,你设计的数据库表一定要有一个自动增长的列,在SQL Server2000或其它的大型数据库中是支持这项功能的。这个是值得引起注意的,不然在更新数据库时候就会有问题了,如果是自己定义的关键字,那就要进行相应的设置,可惜我还没有去探索,时间不允许了。要是有谁知道怎么进行设置,最好在下面回复一下。
***********************************
5,CodeSmith生成的实体类,的属性是要在前面加入virtual关键字的,不然编译器会报错。例子如下(C#代码):*/
public virtual string Lydwh
{
get { return _lydwh; }
set
{
if ( value != null && value.Length > 10)
throw new ArgumentOutOfRangeException("Invalid value for Lydwh", value, value.ToString());
_lydwh = value;
}
}
② 公用的数据层代码(使用写好的类)
③ 生成业务层的代码框架(使用QuickCode插件生成)
//当然也可以用它和一些代码生成工具如CodeSmith生成
//建议还是自己写的好些。
④ 测试代码
我们下面来一步一步看其实现的过程。
1) 创建实体层的类(工具生成)
① 在菜单中选择New建立数据库连接。
② 选择要生成代码的表。
③ 设置生成代码的目录,单击生成。
④ 查看生成结果,如下图:
生成VB.NET代码如下:(相应的C#代码由CodeSmith生成)
Imports System public class Customers Public Sub New()//vb的空构造函数 End Sub 'New private _Region As System.String Public Property Region() As System.String Get return _Region End Get Set(ByVal Value As System.String) _Region = Value End Set End Property private _PostalCode As System.String Public Property PostalCode() As System.String Get return _PostalCode End Get Set(ByVal Value As System.String) _PostalCode = Value End Set End Property private _Fax As System.String Public Property Fax() As System.String Get return _Fax End Get Set(ByVal Value As System.String) _Fax = Value End Set End Property private _ContactName As System.String Public Property ContactName() As System.String Get return _ContactName End Get Set(ByVal Value As System.String) _ContactName = Value End Set End Property private _City As System.String Get return _City End Get Set(ByVal Value As System.String) _City = Value End Set End Property private _CustomerID As System.String Public Property CustomerID() As System.String Get return _CustomerID End Get Set(ByVal Value As System.String) _CustomerID = Value End Set End Property private _Address As System.String Public Property Address() As System.String Get return _Address End Get Set(ByVal Value As System.String) _Address = Value End Set End Property private _ContactTitle As System.String Public Property ContactTitle() As System.String Get return _ContactTitle End Get Set(ByVal Value As System.String) _ContactTitle = Value End Set End Property private _Phone As System.String Public Property Phone() As System.String Get return _Phone End Get Set(ByVal Value As System.String) _Phone = Value End Set End Property private _CompanyName As System.String Public Property CompanyName() As System.String Get return _CompanyName End Get Set(ByVal Value As System.String) _CompanyName = Value End Set End Property private _Country As System.String Public Property Country() As System.String Get return _Country End Get Set(ByVal Value As System.String) _Country = Value End Set End Property End Class |
生成的映射的XML代码如下:
xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"> <class name="ORMDemo.Customers, ORMDemo" table="Customers">//注意这里,在代码中会用到。前面的是持久化类的全限定名,后面的是Assembly的名字。 <id name="CustomerID" column="CustomerID" type="String"> <generator class="assigned" /> id> <property name="CompanyName" type="String(40)" column="CompanyName" /> <property name="ContactName" type="String(30)" column="ContactName" /> <property name="ContactTitle" type="String(30)" column="ContactTitle" /> <property name="Address" type="String(60)" column="Address" /> <property name="City" type="String(15)" column="City" /> <property name="Region" type="String(15)" column="Region" /> <property name="PostalCode" type="String(10)" column="PostalCode" /> <property name="Country" type="String(15)" column="Country" /> <property name="Phone" type="String(24)" column="Phone" /> <property name="Fax" type="String(24)" column="Fax" /> class> hibernate-mapping> |
⑤ 创建一个空的项目,把代码拷贝进去。
⑥ 参照Nhibenate DLL。
⑦ 你现在就可以使用他们来执行数据库的操作了。
2) 公用的数据层代码(使用写好的类)
我们这里使用一个写好的数据库层的访问数据库方法,这样,数据层将不需要再增加任何代码。数据层通用访问方法如下:
//VB 代码 Imports System.Reflection Imports System.Data Imports System.Data.SqlClient Imports NHibernate Imports NHibernate.Cfg Imports NHibernate.Dialect Imports NHibernate.Tool.hbm2ddl Public Class EntityControl Private Shared entity As EntityControl Protected Shared myCfg As NHibernate.Cfg.Configuration Protected Shared dialect As NHibernate.dialect.Dialect Protected Shared sessions As ISessionFactory Public Shared Function CreateControl() As EntityControl If entity Is Nothing Then BuildSessionFactory() If entity Is Nothing Then entity = New EntityControl End If End If Return entity End Function 'CreateControl Private Shared Sub BuildSessionFactory() ExportSchema(New String() {"Customers.hbm.xml"}, False) End Sub 'BuildSessionFactory Public Sub AddEntity(ByVal entity As Object) Dim s As ISession = sessions.OpenSession() Dim t As ITransaction = s.BeginTransaction() Try s.Save(entity) t.Commit() Catch e As Exception t.Rollback() Throw e Finally s.Close() End Try End Sub 'AddEntity Public Sub UpdateEntity(ByVal entity As Object, ByVal key As Object) Dim s As ISession = sessions.OpenSession() Dim t As ITransaction = s.BeginTransaction() Try s.Update(entity, key) t.Commit() Catch e As Exception t.Rollback() Throw e Finally s.Close() End Try End Sub 'UpdateEntity Public Sub DeleteEntity(ByVal entity As Object) Dim s As ISession = sessions.OpenSession() Dim t As ITransaction = s.BeginTransaction() Try s.Delete(entity) t.Commit() Catch e As Exception t.Rollback() Throw e Finally s.Close() End Try End Sub 'DeleteEntity Public Function GetEntities(ByVal query As String) As IList Dim lst As IList Dim s As ISession = sessions.OpenSession() Dim t As ITransaction = s.BeginTransaction() lst = s.Find(query) t.Commit() s.Close() Return lst End Function 'GetEntities Public Function GetEntity(ByVal theType As System.Type, ByVal id As Object) As Object Dim obj As Object Dim s As ISession = sessions.OpenSession() Dim t As ITransaction = s.BeginTransaction() obj = s.Load(theType, id) t.Commit() s.Close() Return obj End Function 'GetEntity Private Overloads Shared Sub ExportSchema(ByVal files() As String) ' ExportSchema(files, True) End Sub 'ExportSchema Private Overloads Shared Sub ExportSchema(ByVal files() As String, ByVal exportSchema As Boolean) myCfg = New NHibernate.Cfg.Configuration Dim i As Integer For i = 0 To files.Length - 1 myCfg.AddResource("ORMDemo." + files(i), [Assembly].Load("ORMDemo")) Next i If exportSchema Then Dim se As New SchemaExport(myCfg) se.Create(True, True) End If sessions = myCfg.BuildSessionFactory() dialect = NHibernate.Dialect.Dialect.GetDialect() End Sub 'ExportSchema '/ '/ Drops the schema that was built with the TestCase's Configuration. '/ Private Shared Sub DropSchema() Dim se As New SchemaExport(myCfg) se.Drop(True, True) End Sub 'DropSchema Private Overloads Shared Sub ExecuteStatement(ByVal sql As String) ExecuteStatement(sql, True) End Sub 'ExecuteStatement Private Overloads Shared Sub ExecuteStatement(ByVal sql As String, ByVal [error] As Boolean) Dim conn As IDbConnection = Nothing Dim tran As IDbTransaction = Nothing Try If myCfg Is Nothing Then myCfg = New NHibernate.Cfg.Configuration End If Dim prov As NHibernate.Connection.IConnectionProvider = NHibernate.Connection.ConnectionProviderFactory.NewConnectionProvider(myCfg.Properties) conn = prov.GetConnection() tran = conn.BeginTransaction() Dim comm As IDbCommand = conn.CreateCommand() comm.CommandText = sql comm.Transaction = tran comm.CommandType = CommandType.Text comm.ExecuteNonQuery() tran.Commit() Catch exc As Exception If Not (tran Is Nothing) Then tran.Rollback() End If If [error] Then Throw exc End If Finally If Not (conn Is Nothing) Then conn.Close() End If End Try End Sub 'ExecuteStatement End Class 'EntityControl |
相应的C#代码如下:
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Tool.hbm2ddl;
using Iesi.Collections ;
using System.Reflection;
using System.Collections;
/// <summary>
/// Summary description for EntityControl
/// </summary>
public class EntityControl
{
private static EntityControl entity ;
protected static NHibernate.Cfg.Configuration myCfg;
protected static NHibernate.Dialect.Dialect dialect;
protected static NHibernate.ISessionFactory sessions;
public EntityControl()
{
//
// TODO: Add constructor logic here
//
}
public static EntityControl CreateControl()
{
if (entity == null )
{
BuildSessionFactory();
if (entity == null )
{
entity = new EntityControl();
}
}
return entity;
}
private static void BuildSessionFactory()
{
ExportSchema(new string[] {"HostMachine.hbm.xml"}, false);
}
private static void ExportSchema(string[] files)
{
ExportSchema(files,true);
}
private static void ExportSchema(string[] files, bool exportSchema)
{
myCfg = new NHibernate.Cfg.Configuration();
int i;
for (i = 0; i <= files.Length - 1; i++)
{
myCfg.AddXmlFile(files[i]);
}
if (exportSchema)
{
SchemaExport se = new SchemaExport(myCfg);
se.Create(true, true);
}
sessions = myCfg.BuildSessionFactory();
dialect = NHibernate.Dialect.Dialect.GetDialect();
}
public void AddEntity(Object entity)
{
ISession s = sessions.OpenSession();
ITransaction t = s.BeginTransaction();
try
{
s.Save(entity);
t.Commit();
}
catch (Exception e)
{
t.Rollback();
throw e;
}
finally
{
s.Close();
}
}
public void UpdateEntity(Object entity, Object key)
{
ISession s = sessions.OpenSession();
ITransaction t = s.BeginTransaction();
try
{
s.Update(entity, key);
t.Commit();
}
catch(Exception e)
{
t.Rollback();
throw e;
}
finally
{
s.Close();
}
}
public void DeleteEntity(Object entity)
{
ISession s = sessions.OpenSession();
ITransaction t = s.BeginTransaction();
try
{
s.Delete(entity);
t.Commit();
}
catch (Exception e)
{
t.Rollback();
throw e;
}
finally
{
s.Close();
}
}
public IList GetEntities(string query)
{
IList lst;
ISession s = sessions.OpenSession();
ITransaction t = s.BeginTransaction();
lst = s.CreateQuery(query).List ();
t.Commit();
s.Close();
return lst;
}
public Object GetEntity(System.Type theType, Object id)
{
Object obj;
ISession s = sessions.OpenSession();
ITransaction t = s.BeginTransaction();
obj = s.Load(theType, id);
t.Commit();
s.Close();
return obj;
}
// Drops the schema that was built with the TestCase's Configuration.
private static void DropSchema()
{
SchemaExport se = new SchemaExport(myCfg);
se.Drop(true,true);
}
private static void ExecuteStatement(string sql)
{
ExecuteStatement(sql, true);
}
private static void ExecuteStatement(string sql,bool error)
{
IDbConnection conn = null;
IDbTransaction tran = null;
try
{
//has errors
if (Object.Equals(myCfg,null ))
{
myCfg = new NHibernate.Cfg.Configuration();
}
NHibernate.Connection.IConnectionProvider prov = NHibernate.Connection.ConnectionProviderFactory.NewConnectionProvider(myCfg.Properties);
conn = prov.GetConnection();
tran = conn.BeginTransaction();
IDbCommand comm = conn.CreateCommand();
comm.CommandText = sql;
comm.Transaction = tran;
comm.CommandType = CommandType.Text;
comm.ExecuteNonQuery();
tran.Commit();
}
catch(Exception exc)
{
if (!Object.Equals(tran, null))
{
tran.Rollback();
}
if (error)
{
throw exc;
}
}
finally
{
if (!(Object.Equals (conn,null )))
{
conn.Close ();
}
}
}
}
3) 生成业务层的代码框架(使用QuickCode插件生成)
我们使用QuickCode可以自动生成,业务层的增删改框架,我们只需要增加业务处理的功能即可。
自动生成的业务层代码如下:
'------------------------------------------------------------------------ ' Copyright (C) 2000-2004 GrapeCity Corporation ' All rights reserved. ' ' Description: ' Customer class ' ' Created By: Tim Wang ' Date Created: 21-09-2004 ' ' Modified History: ' Date By Description ' ---------- ---------------- ------------------------------------- '------------------------------------------------------------------------ Public Class CustomerBR Dim control As EntityControl Sub New()//VB的构造函数 control = EntityControl.CreateControl() End Sub '------------------------------------------------------------------------- ' Name: Add ' ' Description: The Add method is used to Add Customer in BusinessRule layer. ' ' Created By: Tim Wang ' Date Created: 21-09-2004 '-------------------------------------------------------------------------- Public Sub Add(ByVal Customer As Customers) Check(Customer) control.AddEntity(Customer) End Sub '------------------------------------------------------------------------- ' Name: Update ' ' Description: The Update method is used to Update Customer in BusinessRule layer. ' ' Created By: Tim Wang ' Date Created: 21-09-2004 '-------------------------------------------------------------------------- Public Sub Update(ByVal Customer As Customers) control.UpdateEntity(Customer, Customer.CustomerID) End Sub '------------------------------------------------------------------------- ' Name: Delete ' ' Description: The Delete method is used to Delete Customer in BusinessRule layer. ' ' Created By: Tim Wang ' Date Created: 21-09-2004 '-------------------------------------------------------------------------- Public Sub Delete(ByVal Customer As Customers) control.DeleteEntity(Customer) End Sub '------------------------------------------------------------------------- ' Name: GetCustomers ' ' Description: The GetCustomers method is used to Get Customers in BusinessRule layer. ' ' Created By: Tim Wang ' Date Created: 21-09-2004 '-------------------------------------------------------------------------- Public Function GetCustomers(ByVal query As String) As IList Return control.GetEntities(query) End Function '------------------------------------------------------------------------- ' Name: GetSingleCustomer ' ' Description: The GetSingleCustomer method is used to Get a single Customer in BusinessRule layer. ' ' Created By: Tim Wang ' Date Created: 21-09-2004 '-------------------------------------------------------------------------- Public Function GetSingleCustomer(ByVal CustomerID As String) As Customers Return CType(control.GetEntity(GetType(Customers), CustomerID), Customers) End Function Private Sub Check(ByVal Customer As Customers) End Sub End Class |
我们留一个Check方法来做业务数据的检查。(把这个函数直接写入上面的空函数中即可)
如下:
Private Sub Check(ByVal Customer As Customers) If Customer.CustomerID = "911" Then Throw New ApplicationException("911 is not suitable to be used to CustomerID.") End If End Sub |
4) 测试代码
我们来编写调用的测试代码,如下:
Imports NUnit.Framework Public Class TestCustomer Dim Cust As CustomerBR Dim customer As Customers Dim customerID As String Public Sub init() Cust = New CustomerBR customerID = "003" End Sub Public Sub Add() customer = New Customers Dim count As Integer count = Cust.GetCustomers("from c in class Customers").Count With customer .CustomerID = customerID .CompanyName = "GPCT" .Country = " End With Cust.Add(customer) Assert.AreEqual(count + 1, Cust.GetCustomers("from c in class Customers").Count) End Sub Public Sub Modify() customer = Cust.GetSingleCustomer(customerID) Assert.AreEqual(" customer.Country = " Cust.Update(customer) customer = Cust.GetSingleCustomer(customerID) Assert.IsFalse(customer.Country = " Assert.AreEqual(" End Sub Public Sub Delete() customer = New Customers customer.CustomerID = customerID Assert.AreEqual(1, Cust.GetCustomers("from c in class Customers where c.CustomerID = '" & customerID & "'").Count) Cust.Delete(customer) Assert.AreEqual(0, Cust.GetCustomers("from c in class Customers where c.CustomerID = '" & customerID & "'").Count) End Sub Public Sub TestBusinessRule() customer = New Customers With customer .CustomerID = "9111" .CompanyName = "GPCT" .Country = " End With Cust.Add(customer) End Sub End Class |
5) 数据库字段修改演示
我们把Customers表中的Country字段修改为country1,这时候测试代码失败。修改xml文件,再运行测试代码,程序通过。
结论:我们修改字段名称时,只需要修改XML文件即可,程序不做任何改动。
6) 数据库字段增加演示
我们在数据库中增加一字段aaa,重新生成实体类和XML文件,即可使用新的实体类,程序不需要做任何改变。
结论:修改字段时只在需要增加新字段的内容的地方作修改,不需要增加任何其他代码。
7) 数据库字段删除演示
我们删除一列,重新生成实体类和XML文件,程序会在需要修改的地方提示,只要删除此除代码即可,其它程序不做任何改动。
8) 把结果转变成DataSet
如果想把返回的IList转变成DataSet,我们只要调一个通用的方法即可,而如果想从DataSet转变成实体类组成的IList,将会非常复杂。
转换成DataSet的通用的方法如下:
Private Function ConvertToDS(ByVal lst As IList, ByVal typ As System.Type) As DataSet Dim obj As Object Dim ds As New DataSet Dim tbl As DataTable = ds.Tables.Add(typ.Name) ' Get the public properties. Dim myPropertyInfo As System.Reflection.PropertyInfo() = typ.GetProperties((System.Reflection.BindingFlags.Public Or System.Reflection.BindingFlags.Instance)) Dim pi As System.Reflection.PropertyInfo For Each pi In myPropertyInfo tbl.Columns.Add(pi.Name, System.Type.GetType(pi.PropertyType.ToString())) Next For Each obj In lst Dim dr As DataRow = tbl.NewRow For Each pi In myPropertyInfo dr(pi.Name) = pi.GetValue(obj, Nothing) Next tbl.Rows.Add(dr) Next Return ds End Function |
使用上面的方法来生成DataSet的方法如下:
Dim Cust As New CustomerBR Dim customerLst As IList customerLst = Cust.GetCustomers("from Customers") Dim myType As System.Type = GetType(Customers) DataGrid1.DataSource = ConvertToDS(customerLst, myType).Tables(0 |