開篇
平常開發時,由於冗余代碼過多,程序員做重復的工作過多勢必會影響開發效率。倘若對重復性代碼簡單的復制、粘貼,雖然也能節省時間,但也需仔細一步步替換,這無疑也是一件費力的事。這時我們急需代碼生成工具,根據一套Template快速生成我們需要的代碼。代碼生成器原理簡單,完全可以開發一套適合自己的代碼生成器,一個最簡單的代碼生成器,有幾點你需要關注下:
- 查詢系統視圖:INFORMATION_SCHEMA.TABLES、 INFORMATION_SCHEMA.COLUMNS 可以獲得數據庫中表、列的相關信息。
- 字符串的拼接:StringBuilder的使用,其AppendLine()自動換行。
- 將字符串寫入文本文件:File.WriteAllText()
- 使用了部分類(partial)
- 使用可空類型:由於數據庫中表中數據很有可能是NULL,可空類型使得數據從表中讀取出來賦值給值類型更加兼容。
當然自己開發的代碼生成器局限性很大,但對於小項目也是很好的選擇。我也寫過兩篇代碼生成器的拙文,僅供參考。
說起代碼生成器,不得不說Code Smith,基於Template的編程,下面舉例的NTier架構是很基礎的,除了熟悉的三層架構,還生成了抽象工廠、緩存、單例、反射、存儲過程等,當然這個Demo只是學習用,大家可以繼續擴展,打造自己的銅牆鐵壁。
Code Smith
CodeSmith 是一種語法類似於asp.net的基於模板的代碼生成器,程序可以自定義模板,從而減少重復編碼的勞動量,提高效率。Code Smith提供自定義Template,語法也不復雜,類似於asp.net的標識符號,<%%>、<%=%>、<script runat="template">...</script>
Code Smith API
N層架構-實體類模板-Entity Template
- 首先創建一個C# template,創建指令集,導入程序集和名稱空間:
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %>
- 添加屬性Property:
<%--DataSourse--%> <%@ Property Name="CurrentTable" Type="SchemaExplorer.TableSchema" Default="" Optional="False" DeepLoad="True" Category="" Description="" OnChanged="" Editor="" EditorBase="" Serializer="" %> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %>
- 一個簡單的實體類模板內容,可以這樣寫:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace <%=this.RootNamespace%>.Entity { public class <%= CurrentTable.Name%> { <% foreach(ColumnSchema col in CurrentTable.Columns) {%> public <%=col.DataType%> <%=col.Name%> {get;set;} <%}%> } }
當然生成實體類時,需要考慮可空類型,寫好一個Template,以后爽歪歪:)。
N層架構-數據訪問層接口模板,IDao Template
- Script標簽里可以自定義調用的方法,屬性等。數據訪問層接口大家肯定爛熟於心。
- 常用的CRUD方法以及主表找子表,子表找主表。
- Script里面的方法,你需要熟悉一下Code Smith的API,我在上面已經貼出了常用的API,供大家參考。
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="CurrentTable" Type="SchemaExplorer.TableSchema" DeepLoad="True" Optional="False" %> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; using <%=this.RootNamespace%>.Model; namespace <%=this.RootNamespace%>.IDao { public interface I<%= this.CurrentTable.Name %>Dao { int Add(Model.<%= this.CurrentTable.Name %> entity); int Update(Model.<%= this.CurrentTable.Name %> entity); int Delete(Model.<%= this.CurrentTable.Name %> entity); List<Model.<%= this.CurrentTable.Name %>> LoadAllEntities(); Model.<%= this.CurrentTable.Name %> LoadByPK(<%= PKArgsForTable()%>); #region 主表找子表 <% foreach(TableKeySchema fk in this.CurrentTable.ForeignKeys) { %> List<Model.<%=this.CurrentTable.Name%>> Query_<%=this.CurrentTable.Name%>List_By<%=fk.Name%>(<%=this.FKArgsForTable(fk)%>); <%}%> #endregion } } <script runat="template"> //方法:生成列的主鍵列構成的參數列表 public string PKArgsForTable() { string args=""; foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { args += string.Format("{0} {1},",pkCol.Column.DataType,pkCol.Column.Name); } return args.Substring(0,args.Length-1); } //主鍵在子表中的外鍵所包含的列的參數列表帶數據類型 public string FKArgsForTable(TableKeySchema key){ string args=""; foreach(MemberColumnSchema Col in key.ForeignKeyMemberColumns) { args += string.Format("{1} {0},",Col.Column.Name,Col.DataType); } return args.Substring(0,args.Length-1); } </script>
N層架構-抽象工廠-AbstactFactory Template
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False"%> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace <%=this.RootNamespace%>.AbstractFactory { public abstract class DaoFactory { <% foreach(TableSchema table in this.SourceDatabase.Tables) { %> public abstract IDao.I<% = table.Name %>Dao <% = table.Name %>SqlProviderDao { get; } <% } %> } }
N層架構-DaoSqlFactory Template
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False"%> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace <%=this.RootNamespace%>.SqlProviderDao { public class DaoSqlFactory : <%=this.RootNamespace%>.AbstractFactory.DaoFactory { <% foreach(TableSchema table in this.SourceDatabase.Tables) { %> public override <%=this.RootNamespace%>.IDao.I<% = table.Name %>Dao <% = table.Name %>SqlProviderDao { get { //TODO:效率考慮,可考慮使用緩存 SqlProviderDao.<% = table.Name %>SqlProviderDao obj = System.Web.HttpContext.Current.Cache.Get("<%=this.RootNamespace%>.IDao.I<% = table.Name %>Dao") as SqlProviderDao.<% = table.Name %>SqlProviderDao; if (obj == null) { var instance = new SqlProviderDao.<% = table.Name %>SqlProviderDao(); System.Web.HttpContext.Current.Cache.Add("<%=this.RootNamespace%>.IDao.I<% = table.Name %>Dao",instance , null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); //System.Web.HttpRuntime.Cache.Insert( return instance; } return obj; } } <% } %> } }
N層架構-數據訪問層-SqlProviderDao Template
- 數據訪問層(DAAB)可以使用微軟企業級框架 Microsoft Enterprise Library
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="CurrentTable" Type="SchemaExplorer.TableSchema" DeepLoad="True" Optional="False" %> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Practices.EnterpriseLibrary.Data; using System.Data.SqlClient; using <%=this.RootNamespace%>.Model; namespace <%=this.RootNamespace%>.SqlProviderDao { /// <summary> /// 對*<%= this.CurrentTable.Name %>*操作數據庫(針對SQL Server)的實現 /// </summary> public class <%= this.CurrentTable.Name %>SqlProviderDao:IDao.I<%= this.CurrentTable.Name %>Dao { public int Add(Model.<%= this.CurrentTable.Name %> entity) { //通過DataBaseFactory獲得當前數據庫連接對象 Database db=DatabaseFactory.CreateDatabase(); SqlCommand cmd=db.GetStoredProcCommand("usp_<%=this.CurrentTable.Name%>_Insert") as SqlCommand; //cmd參數賦值 <% foreach(ColumnSchema col in this.CurrentTable.Columns) { %> cmd.Parameters.AddWithValue("@<%=col.Name%>",entity.<%=col.Name%>); <%}%> return db.ExecuteNonQuery(cmd); } public int Update(Model.<%= this.CurrentTable.Name %> entity) { //通過DataBaseFactory獲得當前數據庫連接對象 Database db=DatabaseFactory.CreateDatabase(); SqlCommand cmd=db.GetStoredProcCommand("usp_<%=this.CurrentTable.Name%>_Update") as SqlCommand; //cmd參數賦值 <% foreach(ColumnSchema col in this.CurrentTable.Columns) { %> cmd.Parameters.AddWithValue("@<%=col.Name%>",entity.<%=col.Name%>); <%}%> return db.ExecuteNonQuery(cmd); } public int Delete(Model.<%= this.CurrentTable.Name %> entity) { //根據主鍵進行刪除數據,需要考慮出現復合主鍵 //通過DataBaseFactory獲得當前數據庫連接對象 Database db=DatabaseFactory.CreateDatabase(); SqlCommand cmd=db.GetStoredProcCommand("usp_<%=this.CurrentTable.Name%>_Delete") as SqlCommand; //cmd參數賦值 <% foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { %> cmd.Parameters.AddWithValue("@<%=pkCol.Name%>",entity.<%=pkCol.Name%>); <%}%> return db.ExecuteNonQuery(cmd); } public List<Model.<%= this.CurrentTable.Name %>> LoadAllEntities() { //1.通過database工廠得到當前數據庫連接的對象 db Database db = DatabaseFactory.CreateDatabase(); //2.通過db 獲得一個sp構成的cmd SqlCommand cmd = db.GetStoredProcCommand("usp_<% = this.CurrentTable.Name %>_LoadAll") as SqlCommand; //3.通過db執行一個DBReader SqlDataReader reader = db.ExecuteReader(cmd) as SqlDataReader; //4.遍歷DBReader,生成新的Entity對象,然后添加到List List<Model.<%= this.CurrentTable.Name %>> entitiesList=new List<Model.<%= this.CurrentTable.Name %>>(); while(reader.Read()) { entitiesList.Add(new Model.<%=this.CurrentTable.Name%>(){ <%=this.SetValueToProperty %> }); } return entitiesList; } public Model.<%= this.CurrentTable.Name %> LoadByPK(<%= PKArgsForTable()%>) { //1.通過database工廠得到當前數據庫連接的對象 db Database db = DatabaseFactory.CreateDatabase(); //2.通過db 獲得一個sp構成的cmd SqlCommand cmd = db.GetStoredProcCommand("usp_<% = this.CurrentTable.Name %>_LoadByPK") as SqlCommand; <%foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) {%> cmd.Parameters.AddWithValue("@<% = pkCol.Name%>", <% = pkCol.Name%>); <%}%> //3.通過db執行一個DBReader SqlDataReader reader = db.ExecuteReader(cmd) as SqlDataReader; //4.遍歷DBReader,生成新的Entity對象,然后添加到List Model.<%=this.CurrentTable.Name%> one<%=this.CurrentTable.Name%>=null; if(reader.Read()) { one<%=this.CurrentTable.Name%>=new <%=this.CurrentTable.Name%>(){ <%=this.SetValueToProperty %> }; } return one<%=this.CurrentTable.Name%>; } #region ,當前表是子表,主表找子表 <% foreach(TableKeySchema fk in this.CurrentTable.ForeignKeys) { %> public List<Model.<%=this.CurrentTable.Name%>> Query_<%=this.CurrentTable.Name%>List_By<%=fk.Name%>(<%=this.FKArgsForTable(fk)%>) { Database db = DatabaseFactory.CreateDatabase(); SqlCommand cmd = db.GetStoredProcCommand("usp_<% = this.CurrentTable.Name %>_LoadBy_<%= fk.Name%>") as SqlCommand; //賦值:傳遞一個ForeignKey <%=this.SetPropertyValueByFK(fk)%> SqlDataReader reader = db.ExecuteReader(cmd) as SqlDataReader; List<Model.<%= this.CurrentTable.Name %>> entitiesList=new List<Model.<%= this.CurrentTable.Name %>>(); while(reader.Read()) { entitiesList.Add(new Model.<%=this.CurrentTable.Name%>(){ <%=this.SetValueToProperty %> }); } return entitiesList; } <%}%> #endregion } } <script runat="template"> private string SetPropertyValueByFK(TableKeySchema fk) { StringBuilder sb=new StringBuilder(); foreach(MemberColumnSchema fkCol in fk.ForeignKeyMemberColumns) { sb.AppendFormat("cmd.Parameters.AddWithValue(\"@{0}\",{0});\r\n",fkCol.Column.Name); } return sb.ToString(); } private string SetValueToProperty { get { string args=""; foreach(ColumnSchema col in this.CurrentTable.Columns) { args+=string.Format("{0}=({2})reader[\"{1}\"],",col.Name,col.Name,col.DataType); } return args.Substring(0,args.Length-1); } } //方法:生成列的主鍵列構成的參數列表 public string PKArgsForTable() { string args=""; foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { args += string.Format("{0} {1},",pkCol.Column.DataType,pkCol.Column.Name); } return args.Substring(0,args.Length-1); } //主鍵在子表中的外鍵所包含的列的參數列表帶數據類型 public string FKArgsForTable(TableKeySchema key){ string args=""; foreach(MemberColumnSchema Col in key.ForeignKeyMemberColumns) { args += string.Format("{1} {0},",Col.Column.Name,Col.DataType); } return args.Substring(0,args.Length-1); } </script>
N層架構-SP存儲過程-ScriptSp Template
- 創建存儲過程之前首先需要判讀存儲過程是否存在
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False" %> --檢查是否存在 use <%=this.SourceDatabase%> go <% foreach(TableSchema table in this.SourceDatabase.Tables){ %> if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<% = table.Name %>_Insert]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<% = table.Name %>_Insert] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<% = table.Name %>_Update]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<% = table.Name %>_Update] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<% = table.Name %>_Delete]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<% = table.Name %>_Delete] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<% = table.Name %>_LoadAll]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<% = table.Name %>_LoadAll] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<% = table.Name %>_LoadByPK]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<% = table.Name %>_LoadByPK] GO ------------------ <% foreach(TableKeySchema pk in table.ForeignKeys){ %> if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_<%= table.Name %>_LoadBy_<%= pk.Name%>]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[usp_<%= table.Name %>_LoadBy_<%= pk.Name%>] GO <% } %> <% }%> --生成存儲過程 <%foreach(TableSchema table in SourceDatabase.Tables) {%> --當前表示:<%=table.Name%> create proc usp_<%=table.Name%>_Insert ( <%=this.CreateAllColumnsArgs(table)%> ) as insert into <%=table.Name%>(<%=this.CreateAllColumnsWithoutNativeType(table)%>) values(<%=this.CreateAllColumnsArgsWithoutNativeType(table)%>); go -------- create proc usp_<%=table.Name%>_LoadAll as select <%=this.CreateAllColumnsWithoutNativeType(table)%> from <%=table.Name%> go -------- create proc usp_<%=table.Name%>_Delete ( <%=this.CreatePKColumnsArgs(table)%> ) as Delete from <%=table.Name%> where <%=this.CreatePKColumnsArgsWithoutNativeTypeAppendToWhere(table)%>; go -------- create proc usp_<%=table.Name%>_Update ( <%=this.CreateAllColumnsArgs(table)%> ) as update <%=table.Name%> set <%=this.CreateAllColumnsArgsWithoutPK(table)%> where <%=this.CreatePKColumnsArgsWithoutNativeTypeAppendToWhere(table)%>; go --------------- create proc usp_<%=table.Name%>_LoadByPK ( <%=this.CreatePKColumnsArgs(table)%> ) as select <%=this.CreateAllColumnsWithoutNativeType(table)%> from <%=table.Name%> where <%=this.CreatePKColumnsArgsWithoutNativeTypeAppendToWhere(table)%> go ----------主外鍵關系,類的單一職責 <%foreach(TableKeySchema fk in table.ForeignKeys) {%> create proc usp_<%=table.Name%>_LoadBy_<%=fk.Name%> ( <%=this.CreateFK(fk)%> ) as select <%=this.CreateAllColumnsWithoutNativeType(table)%> from <%=table.Name%> where <%=this.CreateFKSetValues(fk,table)%> go <%}%> <%}%> <script runat="template"> //生成所有列的參數列表 private string CreateAllColumnsArgs(TableSchema table) { string args=""; foreach(ColumnSchema col in table.Columns) { if(col.NativeType=="varchar"|| col.NativeType=="nvarchar"||col.NativeType=="char"||col.NativeType=="nchar") { args+=string.Format("@{0} {1}({2}),",col.Name,col.NativeType,col.Size); } else { args+=string.Format("@{0} {1},",col.Name,col.NativeType); } } return args.Substring(0,args.Length-1); } //生成所有列的列表,不帶參數類型 private string CreateAllColumnsWithoutNativeType(TableSchema table) { string args=""; foreach(ColumnSchema col in table.Columns) { args+=string.Format("{0},",col.Name); } return args.Substring(0,args.Length-1); } //生成所有列的參數列表,不帶參數類型 private string CreateAllColumnsArgsWithoutNativeType(TableSchema table) { string args=""; foreach(ColumnSchema col in table.Columns) { args+=string.Format("@{0},",col.Name); } return args.Substring(0,args.Length-1); } //生成PK的參數列表 private string CreatePKColumnsArgs(TableSchema table) { string args=""; foreach(ColumnSchema col in table.Columns) { if(col.IsPrimaryKeyMember) { if(col.NativeType=="varchar"||col.NativeType=="nvarchar"||col.NativeType=="char"||col.NativeType=="nchar") { args+=string.Format("@{0} {1}({2}),",col.Name,col.NativeType,col.Size); } else { args+=string.Format("@{0} {1},",col.Name,col.NativeType); } } } return args.Substring(0,args.Length-1); } //生成主鍵列不帶參數類型Where ID=@ID and... private string CreatePKColumnsArgsWithoutNativeTypeAppendToWhere(TableSchema table) { string args=""; for(int i=0;i<table.PrimaryKey.MemberColumns.Count;i++) { args+=string.Format("{0}=@{0}",table.PrimaryKey.MemberColumns[i].Column.Name); if(i<table.PrimaryKey.MemberColumns.Count-1) { args +=string.Format(" and"); } } return args; } //非主鍵類型參數列表的賦值語句 private string CreateAllColumnsArgsWithoutPK(TableSchema table) { string args=""; foreach(ColumnSchema col in table.NonPrimaryKeyColumns) { args=string.Format("{0}=@{0},",col.Name); } if(table.NonPrimaryKeyColumns.Count==0) { return ""; } return args.Substring(0,args.Length-1); } //主鍵在子表的引用外鍵所包含的列 private string CreateFK(TableKeySchema fk) { string args=""; foreach(MemberColumnSchema fkCol in fk.ForeignKeyMemberColumns) { if(fkCol.NativeType=="varchar"||fkCol.NativeType=="nvarchar"||fkCol.NativeType=="char"||fkCol.NativeType=="nchar") { args+=string.Format("@{0} {1}({2}),",fkCol.Column.Name,fkCol.Column.NativeType,fkCol.Column.Size); } else { args+=string.Format("@{0} {1},",fkCol.Column.Name,fkCol.Column.NativeType); } } return args.Substring(0,args.Length-1); } //根據外鍵表查找子表中多行的Where private string CreateFKSetValues(TableKeySchema fk,TableSchema table) { string args=""; foreach(MemberColumnSchema fkCol in fk.ForeignKeyMemberColumns) { args+=string.Format("{0}.{1}=@{1},",table.Name,fkCol.Column.Name); } return args.Substring(0,args.Length-1); } </script>
N層架構-業務邏輯層-BLLTemplate
- 業務邏輯層上一對多,多對一需要考慮清楚,還要考慮復合主鍵這類情況
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="CurrentTable" Type="SchemaExplorer.TableSchema" DeepLoad="True" Optional="False" %> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace <%=this.RootNamespace%>.BLL { public class <%= CurrentTable.Name%> { private AbstractFactory.DaoFactory dataFactory = DataProvider.DefaultProvider; #region 增 public int Add(Model.<%= CurrentTable.Name%> entity) { //通過工廠獲得當前提供程序 //用反射,通過web.config獲得當前提供程序的名,再實例化 return dataFactory.<%= CurrentTable.Name%>SqlProviderDao.Add(entity); } public int Add(<%=this.AllArgumentsForTable()%>) { return this.Add(new <%=this.RootNamespace%>.Model.<%= CurrentTable.Name%>() { <%=this.AllPropertiesSetValueForTable()%> }); } #endregion #region 改 public int Update(Model.<%= CurrentTable.Name%> entity) { return dataFactory.<%= CurrentTable.Name%>SqlProviderDao.Update(entity); } public int Update(<%=this.AllArgumentsForTable()%>) { return this.Update(new <%=this.RootNamespace%>.Model.<%= CurrentTable.Name%>() { <%=this.AllPropertiesSetValueForTable()%> }); } #endregion #region 刪 public int Delete(Model.<%= CurrentTable.Name%> entity) { return dataFactory.<%= CurrentTable.Name%>SqlProviderDao.Delete(entity); } public int Delete(<%= this.PKArgsForTable()%>) { return this.Delete(new <%=this.RootNamespace%>.Model.<%= CurrentTable.Name%>() { <%=this.PKPropertiesSetValueForTalble()%> }); } #endregion #region Select public Model.<%= CurrentTable.Name%> Get<%= CurrentTable.Name%>ByPK(<%=this.PKArgsForTable()%>) { return dataFactory.<%=this.CurrentTable.Name%>SqlProviderDao.LoadByPK(<%=this.PKArgsWithoutDataTypeForTable()%>); } public List<Model.<%= CurrentTable.Name%>> GetAll<%=this.CurrentTable.Name%>List() { return dataFactory.<%=this.CurrentTable.Name%>SqlProviderDao.LoadAllEntities(); } #endregion #region 當前表如果存在外鍵,找外鍵所在的主表 <% foreach(TableKeySchema fk in this.CurrentTable.ForeignKeys) {%> public Model.<%=fk.PrimaryKeyTable.Name%> FindParentBy<%=fk.Name%>(<%=this.GetFKArgs(fk)%>) { return this.dataFactory.<% = fk.PrimaryKeyTable.Name%>SqlProviderDao.LoadByPK(<%=GetFKArgsWithoutDataType(fk)%>);<% //= PKArgsWithoutDataTypeForTable(fk.PrimaryKeyTable)%> } <%}%> #endregion #region 主表找從表 <% foreach(TableKeySchema pk in this.CurrentTable.PrimaryKeys){ %> //從當前表,找子表的List public List<Model.<%= pk.ForeignKeyTable.Name%>> Get<%= pk.ForeignKeyTable.Name%>ListBy<%= pk.Name%>(<%=this.FKArgsForTable(pk)%>) { return this.dataFactory.<%= pk.ForeignKeyTable.Name%>SqlProviderDao.Query_<%=pk.ForeignKeyTable.Name%>List_By<%=pk.Name%>(<%=this. FKArgsForTableWithoutDataType(pk) %>); } <% } %> #endregion } } <script runat="template"> //方法:根據表,生成所有列構成的參數列表 private string AllArgumentsForTable() { string args=""; foreach(ColumnSchema col in CurrentTable.Columns) { args+=string.Format("{0} {1},",col.DataType,col.Name); } return args.Substring(0,args.Length-1); } //生成實力類時初始化屬性 private string AllPropertiesSetValueForTable() { string args=""; foreach(ColumnSchema col in CurrentTable.Columns) { args+=string.Format("{0}={1},",col.Name,col.Name); } return args.Substring(0,args.Length-1); } //方法:生成列的主鍵列構成的參數列表 private string PKArgsForTable() { string args=""; foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { args+=string.Format("{0} {1},",pkCol.Column.DataType,pkCol.Column.Name); } return args.Substring(0,args.Length-1); } //方法:生成主鍵列的賦值: public string PKPropertiesSetValueForTalble() { string args=""; foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { args+=string.Format("{0}={1},",pkCol.Column.Name,pkCol.Column.Name); } return args.Substring(0,args.Length-1); } //方法:生成主鍵列參數 private string PKArgsWithoutDataTypeForTable() { string args=""; foreach(MemberColumnSchema pkCol in this.CurrentTable.PrimaryKey.MemberColumns) { args+=string.Format("{0},",pkCol.Column.Name); } return args.Substring(0,args.Length-1); } private string PKArgsWithoutDataTypeForTable(TableSchema table) { string args=""; foreach(MemberColumnSchema pkCol in table.PrimaryKey.MemberColumns) { args += string.Format("{0},",pkCol.Column.Name); } return args.Substring(0,args.Length-1); } //方法:按照某個FK,取他的列構成的參數列表(天啊,難道有復合外鍵嗎) public string GetFKArgs(TableKeySchema fk) { string args=""; foreach(MemberColumnSchema fkCol in fk.ForeignKeyMemberColumns) { args+=string.Format("{0} {1},",fkCol.Column.DataType,fkCol.Column.Name); } return args.Substring(0,args.Length-1); } public string GetFKArgsWithoutDataType(TableKeySchema fk) { string args=""; foreach(MemberColumnSchema fkCol in fk.ForeignKeyMemberColumns) { args+=string.Format("{0},",fkCol.Column.Name); } return args.Substring(0,args.Length-1); } //主鍵在子表中的外鍵所包含的列的參數列表 public string FKArgsForTableWithoutDataType(TableKeySchema key){ string args=""; foreach(MemberColumnSchema Col in key.ForeignKeyMemberColumns) { args += string.Format("{0},",Col.Column.Name); } return args.Substring(0,args.Length-1); } //主鍵在子表中的外鍵所包含的列的參數列表帶數據類型 public string FKArgsForTable(TableKeySchema key){ string args=""; foreach(MemberColumnSchema Col in key.ForeignKeyMemberColumns) { args += string.Format("{1} {0},",Col.Column.Name,Col.DataType); } return args.Substring(0,args.Length-1); } </script>
N層架構-單例模式-DefaultProvieder Template
- 單例+反射
<%@ CodeTemplate Inherits="CodeTemplate" Language="C#" TargetLanguage="Text" Description="NetTiers main template." Debug="True" ResponseEncoding="UTF-8"%> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="System.Design" %> <%@ Assembly Name="System.DirectoryServices" %> <%@ Assembly Name="System.Web" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="SchemaExplorer" %> <%@ Import NameSpace="System.IO" %> <%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %> <%-- 1. Datasource --%> <%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False"%> <%@ Property Name="RootNamespace" Default="MyOffice.Models" Type="System.String" Category="Context" Description="TargetTable that the object is based on." %> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace <%=this.RootNamespace%>.BLL { internal class DataProvider { private static <%=this.RootNamespace%>.AbstractFactory.DaoFactory instance = null; static DataProvider() { //.NET能保證這里的代碼只執行一次 //TODO:根據web.config 動態反射實例化數據工廠的實例 //需要:提供程序的DLL文件名 // 類名(完整名稱) 命名空間.類名 string dllfilename = System.Web.Configuration.WebConfigurationManager.AppSettings["DataProviderDllFile"]; string daoFactoryClassName = System.Web.Configuration.WebConfigurationManager.AppSettings["DataProviderFactoryName"]; //抽象工廠 System.Reflection.Assembly dll = System.Reflection.Assembly.LoadFile( System.Web.HttpContext.Current.Server.MapPath("~/DataProviders/" + dllfilename)); instance = dll.CreateInstance(daoFactoryClassName) as <%=this.RootNamespace%>.AbstractFactory.DaoFactory; } private DataProvider() { } public static <%=this.RootNamespace%>.AbstractFactory.DaoFactory DefaultProvider { get { return instance; } } } }
N層架構-主模板-Main Template
- 添加指令集
<%@ Import NameSpace="System.Text" %> <%@ Import NameSpace="System.Text.RegularExpressions" %> <%@ Import NameSpace="System.Diagnostics" %> <%@ Import NameSpace="System.Xml" %> <%@ Import NameSpace="System.Xml.Xsl" %> <%@ Import NameSpace="System.Xml.XPath" %>
- 注冊子模板
<%@ Register Name="EntityClassTemplate" Template="Eyes.Entity.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="BizClassTemplate" Template="Eyes.Biz.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="IDALInterfaceTemplate" Template="Eyes.IDAL.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="DALFactoryTemplate" Template="Eyes.DALFactory.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="DALSqlFactoryTemplate" Template="Eyes.DALSqlFactory.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="SqlSPTemplate" Template="Eyes.SqlSp.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="DataProvider" Template="Eyes.Provider.cst" MergeProperties="False" ExcludeProperties="" %> <%@ Register Name="DALSqlTemplate" Template="Eyes.DALSql.cst" MergeProperties="False" ExcludeProperties="" %> <%--DataSourse--%> <%@ Property Name="ChooseSourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False" Category="01. Getting Started - Required" Description="Database that the tables views, and stored procedures should be based on. IMPORTANT!!! If SourceTables and SourceViews are left blank, the Entire Database will then be generated." %>
- 添加屬性
<%--DataSourse--%> <%@ Property Name="ChooseSourceDatabase" Type="SchemaExplorer.DatabaseSchema" DeepLoad="True" Optional="False" Category="01. Getting Started - Required" Description="Database that the tables views, and stored procedures should be based on. IMPORTANT!!! If SourceTables and SourceViews are left blank, the Entire Database will then be generated." %> <%@ Property Name="RootNamespace" Default="Net.Itcast.CN" Type="System.String" Optional="False"%>
- 調用Script里面的創建模板方法
<%@ Property Name="RootNamespace" Default="Net.Itcast.CN" Type="System.String" Optional="False"%> <%this.SaveEntityClasses();%> <%this.GenerateBizClasses();%> <%this.GenerateIDALInterface();%> <%this.GenerateDALFactory();%> <%this.GenerateDALSqlFactory();%> <%this.GenerateDALSqlTemplate();%> <%this.GenerateDataProvider();%> <%this.GenerateSqlStoredProcedure();%> <script runat="template"> private string templateOutputDirectory=@"C:\Users\thinkpad\Desktop\ERPSolution\ERPSolution"; [Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))] [Optional, NotChecked] [Category("01. Getting Started - Required")] [Description("The directory to output the results to.")] [DefaultValue("")] public string OutputDirectory { get { return templateOutputDirectory; } set { if (value.EndsWith("\\")) value = value.Substring(0, value.Length - 1); templateOutputDirectory = value; } } private void SaveEntityClasses() { CodeTemplate entityTemplate=new EntityClassTemplate(); foreach(TableSchema table in this.ChooseSourceDatabase.Tables) { entityTemplate.SetProperty("CurrentTable",table); entityTemplate.SetProperty("RootNamespace",this.RootNamespace); entityTemplate.RenderToFile(this.templateOutputDirectory+@"\"+this.RootNamespace+@".Model\"+table.Name+".cs",true); Debug.WriteLine(table.Name); } } private void GenerateBizClasses() { CodeTemplate template = new BizClassTemplate(); foreach(TableSchema table in this.ChooseSourceDatabase.Tables) { template.SetProperty("CurrentTable",table); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".BLL\" + table.Name+".cs",true); } } private void GenerateIDALInterface() { CodeTemplate template = new IDALInterfaceTemplate(); foreach(TableSchema table in this.ChooseSourceDatabase.Tables) { template.SetProperty("CurrentTable",table); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".IDAL\" + "I"+table.Name+"Dao"+".cs",true); } } private void GenerateDALSqlTemplate() { CodeTemplate template = new DALSqlTemplate(); foreach(TableSchema table in this.ChooseSourceDatabase.Tables) { template.SetProperty("CurrentTable",table); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".SqlProviderDao\" + table.Name+"SqlDao"+".cs",true); } } private void GenerateDALFactory() { CodeTemplate template = new DALFactoryTemplate(); template.SetProperty("SourceDatabase",this.ChooseSourceDatabase); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".DaoFactory\DaoFactory.cs",true); } private void GenerateDataProvider() { CodeTemplate template = new DataProvider(); template.SetProperty("SourceDatabase",this.ChooseSourceDatabase); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".BLL\DataProvider.cs",true); } private void GenerateDALSqlFactory() { CodeTemplate template = new DALSqlFactoryTemplate(); template.SetProperty("SourceDatabase",this.ChooseSourceDatabase); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".SqlProviderDao\DaoSqlFactory.cs",true); } private void GenerateSqlStoredProcedure() { CodeTemplate template = new SqlSPTemplate(); template.SetProperty("SourceDatabase",this.ChooseSourceDatabase); template.SetProperty("RootNamespace",this.RootNamespace); template.RenderToFile(this.templateOutputDirectory + @"\"+this.RootNamespace+@".SqlSPScript\SqlSPScript.sql",true); } </script>
小結
上面的Ntier Tempalte不是最優化的,還有很多細節需要你考慮,比如數據類型的轉換:
就像我開篇所說那樣,工欲善其事,必先利其器,一套好的模板可以事半功倍,我在這兒拋磚引玉,期望於君共勉,打造屬於自己的銅牆鐵壁。