[NHibernate]從Table產生Mapping File & Domain Class File

如果已經有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個字為大寫,也是另外一個收獲。

原始程式

MappingTool.zip

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^