DIY--记一次改造NHibernate模板的经历

    在经过了基础知识的准备后,下面我们就正式来改造吧。

  我们新建一个模板文件,命名为MYX.cst,OK,下面就开始改造吧: 

  首先我们在头部来些象征性的版权信息,哎。。虽然咱菜,但自己做个样子还是可以的,编写的版权信息如下:         

代码
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两个模板,所以需要在头部进行相关的声明: 

代码
 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  

  下面开始模板主题的编写。首先循环遍历生成所有的字段属性:    

代码
1 namespace <%=Namespace %>
2 {
3 #region <%= ClassName(SourceTable) %>
4 /// <summary>
5 /// Entitiy: <%= ClassName(SourceTable) %> object
      for 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作为后缀,其他的多对多,一对一关系等都进行类似处理,代码如下:

  

代码
<% 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表的生成结果:

  

代码
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 }

   而对应的实体映射文件为:

代码
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标签是这样写的:

代码
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标签可以配对,而之前的标签则只有开始,没有描述,所以应该改成这样: 

代码
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

作者:Rocky翔
出处:http://www.cnblogs.com/RockyMyx/
本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2010-06-08 19:18  RockyXiang  阅读(2291)  评论(18编辑  收藏  举报