如何用T4模板生成數(shù)據(jù)實(shí)體
我們現(xiàn)有的項(xiàng)目沒有采用任何ORM,所有的數(shù)據(jù)讀取與操作都是基于存儲(chǔ)過程的,在代碼端使用 Enterprise Library 5 。 在 EntLib 和數(shù)據(jù)庫之間,是基于我原來寫的一個(gè) T4 實(shí)體生成的模板,之前也沒有詳細(xì)的去整,反正能運(yùn)行出結(jié)果就行了,總之,代碼很亂。
最近一期項(xiàng)目告一段落,后續(xù)項(xiàng)目還沒有上馬,一手把這個(gè)部門建立起來的總監(jiān)(經(jīng)理)又離開了這個(gè)團(tuán)隊(duì),我們幾個(gè)老一批的員工也在思索著是否換換。趁著這個(gè)便當(dāng),我把這個(gè)東西在整出來,算是給我增加一個(gè)砝碼吧。
什么是 T4 模板,自己去搜吧,怎么用也請(qǐng)自己搜吧。懂就懂,不懂我也懶得解釋。
我將要貼出的T4模板是將 SQLServer 2008 的 Table, View , TableType, Procedure 解析為 C# 里的對(duì)應(yīng)實(shí)體,形如下:
Table/View/TableType
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data;
- using System.Collections;
- using System.ComponentModel;
- using System.Runtime.Serialization;
- namespace AsNum.MySecret.Entity.Database {
- /// <summary>
- /// Table : dbo.LoginPolicy
- /// 登陸策略
- /// 數(shù)據(jù)實(shí)體
- /// </summary>
- [Serializable]
- [DataContract]
- public class LoginPolicyEntity {
- /// <summary>
- /// 自動(dòng)編號(hào)
- /// dbo.LoginPolicy.PolicyID
- /// 默認(rèn)值
- /// </summary>
- public int PolicyID{
- get;set;
- }
- private int _UnfreezeTime = 30;
- /// <summary>
- /// 解凍時(shí)間
- /// dbo.LoginPolicy.UnfreezeTime
- /// 默認(rèn)值((30))
- /// </summary>
- public int UnfreezeTime{
- get{
- return _UnfreezeTime;
- }
- set{
- _UnfreezeTime = value;
- }
- }
- private int _MaxFailedCount = 5;
- /// <summary>
- /// 最大失敗次數(shù)
- /// dbo.LoginPolicy.MaxFailedCount
- /// 默認(rèn)值((5))
- /// </summary>
- public int MaxFailedCount{
- get{
- return _MaxFailedCount;
- }
- set{
- _MaxFailedCount = value;
- }
- }
- private bool? _EnableLoginPolicy = true;
- /// <summary>
- /// 是否啟用登陸策略
- /// dbo.LoginPolicy.EnableLoginPolicy
- /// 默認(rèn)值((1))
- /// </summary>
- public bool? EnableLoginPolicy{
- get{
- return _EnableLoginPolicy;
- }
- set{
- _EnableLoginPolicy = value;
- }
- }
- private DateTime _CreateTime = new DateTime();
- /// <summary>
- ///
- /// dbo.LoginPolicy.CreateTime
- /// 默認(rèn)值(getdate())
- /// </summary>
- public DateTime CreateTime{
- get{
- return _CreateTime;
- }
- set{
- _CreateTime = value;
- }
- }
- /// <summary>
- ///
- /// dbo.LoginPolicy.Creator
- /// 默認(rèn)值
- /// </summary>
- public string Creator{
- get;set;
- }
- }
- }
Procedure
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data;
- using System.Collections;
- using System.ComponentModel;
- using System.Data.SqlClient;
- using Microsoft.Practices.EnterpriseLibrary.Data;
- using AsNum.MySecret.Entity.Database;
- using AsNum.Common.Extend;
- namespace AsNum.MySecret.DB {
- ///
- ///
- ///
- public partial class SPs {
- ///
- /// 發(fā)送消息
- ///
- ///
- /// @Title 消息標(biāo)題
- /// @Ctx 消息內(nèi)容
- /// @UserID 用戶ID
- /// @FromIP 發(fā)消息的IP
- /// @IntranetIP 發(fā)消息的內(nèi)網(wǎng)IP,用于擴(kuò)展
- /// @IsPublic 是否公開
- /// @Receiver 消息接收者,表變量
- ///
- public static SqlCommand SendMsg(Database db, string title , string ctx , int? userID , long? fromIP , long? intranetIP , long? isPublic , List receiver){
- if(db == null)
- throw new ArgumentNullException("db");
- SqlCommand sc = new SqlCommand("SendMsg");
- sc.CommandType = CommandType.StoredProcedure;
- db.AddParameter(sc , "@Title" , DbType.String , 100 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , title);
- db.AddParameter(sc , "@Ctx" , DbType.String , 500 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , ctx);
- db.AddParameter(sc , "@UserID" , DbType.Int32 , 4 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , userID);
- db.AddParameter(sc , "@FromIP" , DbType.Int64 , 8 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , fromIP);
- db.AddParameter(sc , "@IntranetIP" , DbType.Int64 , 8 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , intranetIP);
- db.AddParameter(sc , "@IsPublic" , DbType.Int64 , 8 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , isPublic);
- db.AddParameter(sc , "@Receiver" , DbType.Object , -1 , ParameterDirection.Input , true , 0 , 0 , String.Empty , DataRowVersion.Default , receiver.ToDataTable());
- return sc;
- }
- }
- }
可以看到產(chǎn)生的存儲(chǔ)過程調(diào)用方法并沒有獲取到參數(shù)的默認(rèn)值,這個(gè)是因?yàn)?http://msdn.microsoft.com/en-us/library/ms176074.aspx):
SQL Server only maintains default values for CLR objects in this catalog view;
不過,有牛人做出來了,前提是存儲(chǔ)過程沒有加密:
http://www.codeproject.com/KB/database/FindDefaultValueSPParams.aspx
#p#
在聲明 Table / View / TableType 的時(shí)候:
- CREATE TYPE TMessageReceiver AS TABLE(
- SendType VARCHAR(10) NOT NULL,
- Receiver NVARCHAR(128) NOT NULL
- )
- GO
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'消息接收人, 相同SendType 和Recever 只能出現(xiàn)一次' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TYPE',@level1name=N'TMessageReceiver'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'消息發(fā)送類型EML(QQ, MSN留擴(kuò)展)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TYPE',@level1name=N'TMessageReceiver', @level2type=N'COLUMN',@level2name=N'SendType'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'接收地址,跟據(jù)SendType' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TYPE',@level1name=N'TMessageReceiver', @level2type=N'COLUMN',@level2name=N'Receiver'
- GO
------------
- CREATE TABLE LoginPolicy(
- PolicyID INT IDENTITY(1,1) NOT NULL,
- UnfreezeTime INT NOT NULL DEFAULT 30,
- MaxFailedCount INT NOT NULL DEFAULT 5,
- EnableLoginPolicy BIT DEFAULT 1, -- 0 : 不啟用, 1:啟用
- CreateTime DATETIME NOT NULL DEFAULT GETDATE(),
- Creator NVARCHAR(30),
- CONSTRAINT LoginPolicy_PK PRIMARY KEY (PolicyID)
- )
- GO
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'登陸策略' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'LoginPolicy'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'自動(dòng)編號(hào)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'LoginPolicy', @level2type=N'COLUMN',@level2name=N'PolicyID'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'解凍時(shí)間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'LoginPolicy', @level2type=N'COLUMN',@level2name=N'UnfreezeTime'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'最大失敗次數(shù)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'LoginPolicy', @level2type=N'COLUMN',@level2name=N'MaxFailedCount'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否啟用登陸策略' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'LoginPolicy', @level2type=N'COLUMN',@level2name=N'EnableLoginPolicy'
- GO
聲明存儲(chǔ)過程:
- CREATE PROCEDURE SendMsg
- @Title NVARCHAR(100),
- @Ctx NVARCHAR(500),
- @UserID INT,
- @FromIP BIGINT,
- @IntranetIP BIGINT,
- @IsPublic BIGINT,
- @Receiver TMessageReceiver READONLY
- AS
- BEGIN
- -- 表變量,用以存儲(chǔ)新增的主表ID
- DECLARE @T AS TABLE (ID INT)
- BEGIN TRAN NewMsg
- BEGIN TRY
- -- 寫入主表
- INSERT INTO [Message]
- (Title, Ctx, UserID, FromIP, IntranetIP, IsPublic)
- OUTPUT INSERTED.MessageID INTO @T
- VALUES
- (@Title, @Ctx, @UserID, @FromIP, @IntranetIP , @IsPublic )
- -- 取出新增數(shù)據(jù)的ID
- DECLARE @MessageID INT
- SELECT TOP 1 @MessageID = ID FROM @T
- -- 寫子表, 這里要改動(dòng)一下,相同的只保留一條
- INSERT INTO MessageReceiver
- (MessageID, SendType, Receiver )
- SELECT
- @MessageID, R.SendType, R.Receiver
- FROM
- @Receiver R
- END TRY
- BEGIN CATCH
- ROLLBACK TRAN NewMsg
- RETURN 2 -- DatabaseError
- END CATCH
- COMMIT TRAN NewMsg
- RETURN 0
- END
- GO
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'發(fā)送消息' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'消息標(biāo)題' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@Title'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'消息內(nèi)容' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@Ctx'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用戶ID' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@UserID'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'發(fā)消息的IP' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@FromIP'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'發(fā)消息的內(nèi)網(wǎng)IP,用于擴(kuò)展' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@IntranetIP'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否公開' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@IsPublic'
- EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'消息接收者,表變量' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'SendMsg', @level2type=N'PARAMETER', @level2name = '@Receiver'
- GO
這些 MS_Description 的 value 會(huì)做為注釋寫到生成的代碼里。
實(shí)現(xiàn)T4模板就很簡單了,就是把下面幾個(gè)SQL的結(jié)果取出來,在自由的組合一下:
- WITH Entities AS (
- SELECT
- Table_catalog AS [Database]
- , table_schema [Owner]
- , table_name [Name]
- , 'Table' [Type]
- ,OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME) ID
- FROM
- INFORMATION_SCHEMA.TABLES
- WHERE
- TABLE_TYPE='BASE TABLE'
- UNION ALL
- SELECT
- Table_catalog
- , table_schema
- , table_name
- , 'View'
- , OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME) ID
- FROM
- information_schema.views
- UNION ALL
- SELECT
- DB_NAME()
- , SCHEMA_NAME(schema_id)
- , name
- , 'TableType'
- , user_type_id
- FROM
- sys.table_types
- )
- SELECT
- E.*
- , P.value AS [DESC]
- FROM
- Entities E
- LEFT JOIN sys.extended_properties P ON E.ID = P.major_id AND P.minor_id = 0
Table / View / TableType
字段:
- SELECT
- DB_NAME() AS [Database]
- ,COALESCE( SCHEMA_NAME(T.Schema_id), OBJECT_SCHEMA_NAME(C.object_id)) AS Owner
- ,COALESCE( T.Name, OBJECT_NAME(c.OBJECT_ID)) AS Parent
- ,C.column_id AS OrdinalPosition
- ,C.name AS Name
- ,TYPE_NAME(c.user_type_id) AS DataType
- ,D.definition AS DefaultSetting
- , C.is_nullable AS IsNullable
- , C.max_length AS MaxLength
- , COLUMNPROPERTY( C.OBJECT_ID , C.Name ,'PRECISION') AS [Precision] -- 用于判斷NVARCHAR 實(shí)際長度的
- --,C.Object_id, P.major_id, P.minor_id
- ,P.Value AS [DESC]
- FROM
- sys.columns C
- LEFT JOIN sys.table_types T ON T.type_table_object_id = C.object_ID
- LEFT JOIN sys.default_constraints D ON C.object_id = D.parent_object_id AND D.parent_column_id = C.Column_id
- LEFT JOIN sys.extended_properties P ON (P.major_id = C.OBJECT_ID OR P.major_id = T.User_type_id ) AND P.minor_id = C.COLUMN_ID AND P.name = 'MS_Description'
- WHERE
- COALESCE( SCHEMA_NAME(T.Schema_id), OBJECT_SCHEMA_NAME(C.object_id)) = 'dbo'
存儲(chǔ)過程:
- SELECT
- SCHEMA_NAME(P.schema_id) AS Owner
- , P.Name
- , PP.Value AS [Desc]
- FROM
- sys.procedures P
- LEFT JOIN sys.extended_properties PP ON P.object_id = PP.major_id AND PP.minor_id = 0
存儲(chǔ)過程的參數(shù):
- SELECT
- SCHEMA_NAME( P.[schema_id] ) AS [Schema]
- ,P.Name AS [Proc]
- ,PA.Name
- , (SELECT COUNT(1) FROM sys.table_types WHERE user_type_id = PA.user_type_id) AS IsTableType
- ,TYPE_NAME(PA.user_type_id) AS DataType
- , PA.max_length AS MaxLength
- , COLUMNPROPERTY( PA.OBJECT_ID , PA.Name ,'PRECISION') AS [Precision] -- 用于判斷NVARCHAR 實(shí)際長度的
- , PA.is_output AS IsOutput
- , PP.value AS [Desc]
- FROM
- sys.procedures P
- INNER JOIN sys.parameters PA ON P.object_id = PA.object_id
- LEFT JOIN sys.extended_properties PP ON PA.object_id = PP.major_id AND PA.parameter_id = PP.minor_id
- ORDER BY
- PA.Object_id, PA.Parameter_id
源代碼包:http://files.cnblogs.com/xling/ExecuteT4.7z
這個(gè)包里有一個(gè)如何用程序運(yùn)行T4模板的示例,加這個(gè)是因?yàn)橥聠栁胰绾斡么a去執(zhí)行T4模板,昨晚K歌完到家12點(diǎn)半(跟經(jīng)理道別),洗完澡后,我從以前寫的代碼里翻出來的,稍稍做了點(diǎn)修改。還有一份數(shù)據(jù)字典導(dǎo)出模板.
另外需要注意的是:
1, Microsoft.VisualStudio.TextTemplating.dll 這個(gè) DLL,如果你裝的是VS2010 就默認(rèn)裝了這個(gè)。如果是VS2008, 需要從:Visual Studio 2008 SDK 1.1 里找
2, 如圖
遺留的問題:
有些默認(rèn)值沒有處理好,比如對(duì)DateTime字段只處理了這種情況: GETDATE() 或 ‘2011-01-01’ , 其它的我暫時(shí)沒有遇到,沒有做處理。
存儲(chǔ)過程的 TableType 參數(shù),我簡單的映射為 DbType.Object ,還沒有驗(yàn)證這樣是否可行。
后面我會(huì)把這些都修正的,如果你有興趣的話,可以留意。
原文鏈接:http://www.cnblogs.com/xling/archive/2011/05/24/2055503.html