DIY--记一次改造NHibernate模板的经历
在经过了基础知识的准备后,下面我们就正式来改造吧。
我们新建一个模板文件,命名为MYX.cst,OK,下面就开始改造吧:
首先我们在头部来些象征性的版权信息,哎。。虽然咱菜,但自己做个样子还是可以的,编写的版权信息如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #region Copyright
2
3 ////////////////////////////////////////////////////////////////
4
5 //Date: <%=DateTime.Now.ToString() %>
6
7 //Author: Rocky_Myx
8
9 //Blog: http://www.cnblogs.com/RockyMyx
10
11 ////////////////////////////////////////////////////////////////
12
13 #endregion
声明部分: 由于我主要是改造NHibernate的实体类生成代码,而最后生成的代码还要配合原来的NHibernate.cst和NHibernate.hbm.cst两个模板,所以需要在头部进行相关的声明:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <%@ CodeTemplate Language="C#"
TargetLanguage="C#"
Description="Generates a C# class for use with NHibnate" %>
2 <%@ Property Name="SourceTable"
Type="SchemaExplorer.TableSchema"
Category="Context"
Description="Table that the mapping file is based on" %>
3 <%@ Property Name="Namespace"
Type="System.String"
Default="MyNamespace.Data"
Category="Object"
Description="The class namespace that the mapping file should use" %>
4 <%@ Property Name="Assembly"
Type="System.String"
Default="MyApp.MyAssembly"
Category="Object"
Description="The assembly that the class will be used in" %>
5 <%@ Property Name="RemoveTablePrefix"
Type="System.String"
Default="tbl"
Category="Object"
Description="The prefix to remove from table names" %>
6 <%@ Property Name="ForceId"
Type="System.Boolean"
Default="true"
Category="Object"
Description="Force Id for identity column" %>
<%--加载使用访问数据库的组件SchemaExplorer --%>
7 <%@ Assembly Name="SchemaExplorer" %>
8 <%@ Assembly Name="System.Data" %>
9 <%@ Import Namespace="SchemaExplorer" %>
10 <%@ Import Namespace="System.Data" %>
11 <%@ Import Namespace="System.Text.RegularExpressions" %>
随后添加实体文件所需的命名空间,如下:
1 #region using
2
3 using System;
4
5 using System.Collections;
6
7 using System.Collections.Generic;
8
9 using System.Runtime.Serialization;
10
11 #endregion
12
下面开始模板主题的编写。首先循环遍历生成所有的字段属性:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 namespace <%=Namespace %>
2 {
3 #region <%= ClassName(SourceTable) %>
4 /// <summary>
5 /// Entitiy: <%= ClassName(SourceTable) %> objectfor NHibernate mapped table
6 /// </summary>
7 [DataContract]
8 public partial class <%= ClassName(SourceTable) %>
9 {
10 <% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
11 #region <%= SourceTable.Columns[i].Name %>
12
13 /// <summary>
14 /// Field: <%= SourceTable.Columns[i].Name %>
15 /// </summary>
16 [DataMember]
17 public virtual <%=CSharpType(SourceTable.Columns[i])%><%= SourceTable.Columns[i].Name %> { get; set; }
18
19 #endregion
20
21 <% } %>
22 }
23 #endregion
24 }
25
这里依然用到了ClassName () ,CsharpType()等方法,所以需要引入NHibernate.inc的文件支持,所以在本模板的最后声明相关引用如下:
<!-- #include file="NHibernate.inc" -->
这样就完成了模板中最重要的部分,不过这还没有结束,因为有关联的表还需要建立映射关系,这里约定所有一对多的关系通通以List作为命名后缀,而多对一的关系通通使用Entity作为后缀,其他的多对多,一对一关系等都进行类似处理,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<% foreach(TableKeySchema foreignKey in SourceTable.ForeignKeys) { %>
#region ManyToOneMapping
[DataMember]
public virtual <%= ManyToOneClass(foreignKey) %> <%
= ManyToOneName(foreignKey) %>Entity { get; set; }
#endregion
<% if ((foreignKey.ForeignKeyTable == SourceTable) &&
(foreignKey.PrimaryKeyTable == SourceTable)) { %>
#region OneToManyMapping
[DataMember]
public virtual <%= CollectionType(foreignKey) %><<%=
CollectionName(foreignKey) %>> <%= CollectionName
(foreignKey) %>List { get; set; }
#endregion
<% } %>
<% } %>
<% foreach(TableKeySchema primaryKey in
SourceTable.PrimaryKeys) { %>
<% if (IsManyToManyTable(primaryKey.ForeignKeyTable)) { %>
#region ManyToManyMapping
[DataMember]
public virtual <%= CollectionType(primaryKey) %><<%=
CollectionManyToManyClass(primaryKey) %>> <%=
CollectionManyToManyClass(primaryKey) %>List { get;
set; }
#endregion
<% } else if (IsOneToOneTable(primaryKey)) { %>
#region OneToOneMapping
[DataMember]
public virtual <%= OneToOneClass(primaryKey) %> <%
= OneToOneName(primaryKey) %>Entity { get; set; }
#endregion
<% } else { %>
#region OneToManyMapping
[DataMember]
public virtual <%= CollectionType(primaryKey) %><<%=
CollectionName(primaryKey) %>> <%= CollectionName
(primaryKey) %>List { get; set; }
#endregion
<% } %>
<% } %>
由于这里我们使用自己的约定,所以原来NHibernate.inc文件里的MakePlural和MakeSingle我们就不再使用了。
而xxx.hbm.xml的映射文件为了和我们约定的名称对应,因此也要进行相应的更改,下载的demo里在所有改动的语句上都加了//changed标记,有兴趣的可以下下来瞧瞧。
而至于生成映射文件的NHibernate.hbm.cst模板,为了版本的需要,我只是将原来头部的<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">改成了<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">。
好了,大功告成了,不过最后别忘了在启动的NHibernate.cst模板中,将第54行实体类文件的生成模板改成MYX.cst。
OK,现在设置下NHibernate.cst模板的相关属性,包括输出路径,预定义的程序集名称,命名空间名称,是否强制有ID列,以及是否要移除表的前缀。
这里我选择了Northwind数据库进行测试,填写完相关属性后,按下期待已久的F5,终于得到了我想要的代码。
下面是示例数据库Northwind的Category表的生成结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #region Copyright
2 ///////////////////////////////////////////////////////////////////////
3 //Date: 2010/6/8 19:07:16
4 //Author: Rocky_Myx
5 //Blog: http://www.cnblogs.com/RockyMyx
6 //Description: This template was reformed by MYX
7 ///////////////////////////////////////////////////////////////////////
8 #endregion
9
10 #region using
11 using System;
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.Runtime.Serialization;
15 #endregion
16
17 namespace
18 {
19 #region Categories
20
21 /// <summary>
22 /// Categories object for NHibernate mapped table 'Categories'.
23 /// </summary>
24 [DataContract]
25 public partial class Categories
26 {
27 #region CategoryID
28
29 /// <summary>
30 /// Field: CategoryID
31 /// </summary>
32 [DataMember]
33 public virtual int CategoryID { get; set; }
34
35 #endregion
36 #region CategoryName
37
38 /// <summary>
39 /// Field: CategoryName
40 /// </summary>
41 [DataMember]
42 public virtual string CategoryName { get; set; }
43
44 #endregion
45 #region Description
46
47 /// <summary>
48 /// Field: Description
49 /// </summary>
50 [DataMember]
51 public virtual string Description { get; set; }
52
53 #endregion
54 #region Picture
55
56 /// <summary>
57 /// Field: Picture
58 /// </summary>
59 [DataMember]
60 public virtual byte[] Picture { get; set; }
61
62 #endregion
63 #region OneToManyMapping
64 [DataMember]
65 public virtual IList<Products> ProductsList {
66 get; set; }
67 #endregion
68 }
69 #endregion
70 }
而对应的实体映射文件为:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
3 <class name=".Categories, " table="Categories">
4 <id name="Id" type="Int32" unsaved-value="null">
5 <column name="CategoryID" length="4" sql-type="int"
6 not-null="true" unique="true" index="PK_Categories"/>
7 <generator class="native" />
8 </id>
9
10 <property name="CategoryName" type="String">
11 <column name="CategoryName" length="15" sql-
12 type="nvarchar" not-null="true"
13 index="CategoryName"/>
14 </property>
15
16 <property name="Description" type="String">
17 <column name="Description" length="16" sql-type="ntext" not-null="false"/>
18 </property>
19
20 <property name="Picture" type="Byte[]">
21 <column name="Picture" length="16" sql-type="image" not-null="false"/>
22 </property>
23
24 <bag name="ProductsList" inverse="true" lazy="true" cascade="all-delete-orphan">
25 <key column="CategoryID"/>
26 <one-to-many class=".Products, "/>
27 </bag>
28
29 </class>
30 </hibernate-mapping>
看上去很完美,从这里的生成结果来看貌似也的确是这样,但是事情总归不会一帆风顺,虽然我不是一个很有怀疑精神的人,但是我发现配置文件的模板,也就是NHibernate.hbm.cst在生成多对多的映射关系时是有问题的,如果你没有改动过这个模板,那就是在NHibernate.hbm.cst中的第59行,这里的many-to-many标签是这样写的:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <many-to-many<%=CollectionManyToManyClassAtt(tableKey)%>>
2 <% foreach(ColumnSchema column in tableKey.ForeignKeyMemberColumns) { %>
3 <column<%= ColumnNameAtt(column) %><%= ColumnLengthAtt(column) %><%= ColumnSqlTypeAtt(column) %><%= ColumnNotNullAtt(column) %><%= ColumnUniqueAtt(column) %><%= ColumnIndexAtt(SourceTable, column) %>/>
4 <% } %>
5 <% } %>
6 <% } %>
7 </many-to-many>
8
如果这里只是一个多对多的关系,那么标签肯定会配对,不会出现xml标签不闭合的语法错误,但是如果这里有多个这样的多对多关系,那么生成的配置文件中,最后一个many-to-many标签可以配对,而之前的标签则只有开始,没有描述,所以应该改成这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <many-to-many<%=CollectionManyToManyClassAtt(tableKey)%>>
2 <% foreach(ColumnSchema column in tableKey.ForeignKeyMemberColumns) { %>
3 <column<%= ColumnNameAtt(column) %><%=
4 ColumnLengthAtt(column) %><%= ColumnSqlTypeAtt
5 (column) %><%= ColumnNotNullAtt(column) %><%=
6 ColumnUniqueAtt(column) %><%= ColumnIndexAtt
7 (SourceTable, column) %>/>
8 <% } %>
9 <% } %>
10 </many-to-many>
11 <% } %>
12
这样就可以保证多个这样的标签也能配对了,OK,更改过后偶菜菜的模板算是告一段落了。
需要代码的筒子来这里下载吧:NHibernate Template
出处:http://www.cnblogs.com/RockyMyx/
本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。