如果已經有Table了,要產生NHibernate的Mapping File一個一個欄位寫總是很麻煩,重點是,可能會寫錯!
所以就用程式來Gen吧!
前言
如果已經有Table了,要產生NHibernate的Mapping File一個一個欄位寫總是很麻煩,重點是,可能會寫錯!
如果有一定的規則就可以從Table產出Mapping File及Domain Class。
環境
VS2010, NHibernate, SQL Server
說明
我們的Table Name及Column Name都是用大寫然後各單字用底線分隔,如下,
CREATE TABLE [dbo].[ALBUM_INFO] ( [GID] [NVARCHAR](255) NOT NULL, [TITLE] [nvarchar](160) NOT NULL, [PRICE] [numeric](10, 2) NOT NULL, [ALBUM_DESC] [nvarchar](1024) NULL, CONSTRAINT [PK_ALBUM] PRIMARY KEY CLUSTERED ([GID] ASC) )
整理規則
我們把就Domain名稱取成AlbumInfo, 第一個字大寫,其他的小寫,Map到Mapping File。
<class name ="Domain.@CLASS_NAME@, Domain" table="@TABLE_NAME@" lazy="false">
<property name="Property" column="DBColumn" type="String" length="50" not-null="false"/>
需求
1.先建立Mapping File及Domain Class的文字範本檔
2.寫Win Form AP,連到DB,取出該DB的Table List
3.取出Table的詳細資料(字串形態、長度、是否允許Null)
4.將Table資訊依規則套到範本之中,然後輸出存檔。
實作
1.先建立Mapping File及Domain Class的文字範本檔
NHibernate Mapping File如下,
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class name ="Domain.@CLASS_NAME@, Domain" table="@TABLE_NAME@" lazy="false"> <id name="Pk" type="String" column="GID"> <generator class="uuid.hex" /> </id> <!-- 欄位自動產生的Mapping --> @FILED_LIST@ <!-- 欄位自動產生的Mapping --> </class> </hibernate-mapping>
Domain Class範本如下,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; namespace Domain { public class @CLASS_NAME@ : Entity { @FILED_LIST@ } }
2.寫Win Form AP,連到DB,取出該DB的Table List
透過SqlConnectionStringBuilder建立出ConnectionString連到DB, 然後利用Connection的GetSchema("Tables")取得Table的資訊。
/// <summary> /// 取出ConnectiongString /// </summary> /// <returns></returns> private string GetConnectionString() { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); builder.ApplicationName = "MappingTool"; builder.DataSource = txtDBServer.Text.Trim(); //dbserver builder.InitialCatalog = txtDBName.Text.Trim(); //db builder.UserID = txtUsrId.Text.Trim(); builder.Password = txtPassword.Text.Trim(); return builder.ConnectionString; }
using (SqlConnection con = new SqlConnection(GetConnectionString())) { try { con.Open(); DataTable dtTableList = con.GetSchema("Tables"); DataView dvList = new DataView(dtTableList); //依Table Name排序 dvList.Sort = "TABLE_NAME"; foreach (DataRowView drv in dvList) { //將Table Name加到ListBox之中 lstTables.Items.Add(drv["TABLE_NAME"]); } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } }
3.取出Table的詳細資料(字串形態、長度、是否允許Null)
取Table的Schema資訊,可下Select * from TableName Where 1= 0,然後透過DataReader的GetSchemaTable()取得Schema的資訊,這裡我們要知道的有ColumnName, AllowDBNull, DataType, ColumnSize
//取出範本的字串 string hbmTmpString = GetTmpFileString(); string domainClassTmpString = GetDomainClassTmpFileString(); string tableSQL = @"SELECT * FROM {0} WHERE 1 =0"; string strColumnTmp = @"<property name=""{0}"" column=""{1}"" type=""String"" length=""{2}"" {3} />"; string nonStrColumnTmp = @"<property name=""{0}"" column=""{1}"" type=""{2}"" {3} />"; string classFieldTmp = @"public {0}{1} {2} {{ get; set; }}"; using (SqlConnection con = new SqlConnection(GetConnectionString())) { try { con.Open(); //先取出範本的資料 foreach (string tableName in lstTables.SelectedItems) { //取得TALBE的SHEMA SqlCommand cmd = new SqlCommand(string.Format(tableSQL, tableName), con); SqlDataReader dbDR = cmd.ExecuteReader(CommandBehavior.KeyInfo); DataTable tableSchema = dbDR.GetSchemaTable(); dbDR.Close(); StringBuilder sbFileds = new StringBuilder(); StringBuilder sbDomainFields = new StringBuilder(); foreach (DataRow dr in tableSchema.Rows) { // GID這些欄位不用gen if ( dr["ColumnName"] as string == "GID") { // do nothing } else { //sbDomainFields.Append("[DataMember]"); for WCF sbDomainFields.Append(System.Environment.NewLine); string allowDBNull = (bool)dr["AllowDBNull"] ? @"not-null=""false""" : @"not-null=""true"""; string propName = ConvertDB2Class(dr["ColumnName"] as string); string filedDeclare; string domainFieldDeclare; if ((dr["DataType"] as Type).Name.ToLower() != "string") { //非字串 filedDeclare = string.Format(nonStrColumnTmp, propName, dr["ColumnName"] as string, (dr["DataType"] as Type).Name, allowDBNull); domainFieldDeclare = string.Format(classFieldTmp, (dr["DataType"] as Type).Name, (bool)dr["AllowDBNull"] ? "?" : string.Empty, propName); } else { //字串 filedDeclare = string.Format(strColumnTmp, propName, dr["ColumnName"] as string, dr["ColumnSize"].ToString(), allowDBNull); domainFieldDeclare = string.Format(classFieldTmp, (dr["DataType"] as Type).Name, string.Empty, propName); } sbFileds.Append(filedDeclare); sbFileds.Append(System.Environment.NewLine); sbDomainFields.Append(domainFieldDeclare); sbDomainFields.Append(System.Environment.NewLine); } } //針對檔案replace string className = ConvertDB2Class(tableName); string desString = hbmTmpString.Replace(@"@TABLE_NAME@", tableName).Replace(@"@CLASS_NAME@", className); desString = desString.Replace(@"@FILED_LIST@", sbFileds.ToString()); Save2File(string.Format("{0}.hbm.xml", className), desString); //處理domain class string domainDesString = domainClassTmpString.Replace(@"@CLASS_NAME@", className).Replace(@"@FILED_LIST@", sbDomainFields.ToString()); Save2File(string.Format("{0}.cs", className), domainDesString); } MessageBox.Show("OK"); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } }
4.將Table資訊依規則套到範本之中,然後輸出存檔。
要將字串第1個字轉大寫,可透過TextInfo的ToTitleCase來達到這個目的。
private string ConvertDB2Class(string dbString) { StringBuilder result = new StringBuilder(); // Creates a TextInfo based on the "en-US" culture. TextInfo myTI = new CultureInfo("en-US", false).TextInfo; string[] arySplit = dbString.Split('_'); foreach (string item in arySplit) { result.Append(myTI.ToTitleCase(item.ToLower())); } return result.ToString(); }
之後就產生如下的Mapping File & Domain Class File
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class name ="Domain.Album, Domain" table="ALBUM" lazy="false"> <id name="Pk" type="String" column="GID"> <generator class="uuid.hex" /> </id> <!-- 欄位自動產生的Mapping --> <property name="Title" column="TITLE" type="String" length="160" not-null="true" /> <property name="Price" column="PRICE" type="Decimal" not-null="true" /> <property name="AlbumDesc" column="ALBUM_DESC" type="String" length="1024" not-null="false" /> <!-- 欄位自動產生的Mapping --> </class> </hibernate-mapping>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; using SkyPea.Generic.Domain.Impl; namespace SkyPea.Domain.LOL { [DataContract] public class Album : Entity { public String Title { get; set; } public Decimal Price { get; set; } public String AlbumDesc { get; set; } } }
結論
以上只是產生基本的mapping file出來,如果各Table間有關連的話,要另外手動加入哦!
透過以上的範例,還學習到使用SqlConnectionStringBuilder物件來組出Connection String,透過Connection物件GetSchema取得DB中的Table清單,DataReader的GetSchemaTable取得Table詳細資料,TextInfo物件的ToTitleCase Method該字串第1個字為大寫,也是另外一個收獲。
原始程式
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^