首页 运维 正文
如何用T4模板生成数据实体

 2022-10-23    332  

我们现有的项目没有采用任何ORM,所有的数据读取与操作都是基于存储过程的,在代码端使用 Enterprise Library 5 。 在 EntLib 和数据库之间,是基于我原来写的一个 T4 实体生成的模板,之前也没有详细的去整,反正能运行出结果就行了,总之,代码很乱。

最近一期项目告一段落,后续项目还没有上马,一手把这个部门建立起来的总监(经理)又离开了这个团队,我们几个老一批的员工也在思索着是否换换。趁着这个便当,我把这个东西在整出来,算是给我增加一个砝码吧。

什么是 T4 模板,自己去搜吧,怎么用也请自己搜吧。懂就懂,不懂我也懒得解释。

我将要贴出的T4模板是将 SQLServer 2008 的 Table, View , TableType, Procedure 解析为 C# 里的对应实体,形如下:

Table/View/TableType

usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; 
usingSystem.Text; 
usingSystem.Data; 
usingSystem.Collections; 
usingSystem.ComponentModel; 
usingSystem.Runtime.Serialization; 
namespaceAsNum.MySecret.Entity.Database{ 
///<summary> 
///Table:dbo.LoginPolicy 
///登陆策略 
///数据实体 
///</summary> 
[Serializable] 
[DataContract] 
publicclassLoginPolicyEntity{ 
///<summary> 
///自动编号 
///dbo.LoginPolicy.PolicyID 
///默认值 
///</summary> 
publicintPolicyID{ 
get;set; 
} 
privateint_UnfreezeTime=30; 
///<summary> 
///解冻时间 
///dbo.LoginPolicy.UnfreezeTime 
///默认值((30)) 
///</summary> 
publicintUnfreezeTime{ 
get{ 
return_UnfreezeTime; 
} 
set{ 
_UnfreezeTime=value; 
} 
} 
privateint_MaxFailedCount=5; 
///<summary> 
///最大失败次数 
///dbo.LoginPolicy.MaxFailedCount 
///默认值((5)) 
///</summary> 
publicintMaxFailedCount{ 
get{ 
return_MaxFailedCount; 
} 
set{ 
_MaxFailedCount=value; 
} 
} 
privatebool?_EnableLoginPolicy=true; 
///<summary> 
///是否启用登陆策略 
///dbo.LoginPolicy.EnableLoginPolicy 
///默认值((1)) 
///</summary> 
publicbool?EnableLoginPolicy{ 
get{ 
return_EnableLoginPolicy; 
} 
set{ 
_EnableLoginPolicy=value; 
} 
} 
privateDateTime_CreateTime=newDateTime(); 
///<summary> 
/// 
///dbo.LoginPolicy.CreateTime 
///默认值(getdate()) 
///</summary> 
publicDateTimeCreateTime{ 
get{ 
return_CreateTime; 
} 
set{ 
_CreateTime=value; 
} 
} 
///<summary> 
/// 
///dbo.LoginPolicy.Creator 
///默认值 
///</summary> 
publicstringCreator{ 
get;set; 
} 
} 
}

Procedure

usingSystem; 
usingSystem.Collections.Generic; 
usingSystem.Linq; 
usingSystem.Text; 
usingSystem.Data; 
usingSystem.Collections; 
usingSystem.ComponentModel; 
usingSystem.Data.SqlClient; 
usingMicrosoft.Practices.EnterpriseLibrary.Data; 
usingAsNum.MySecret.Entity.Database; 
usingAsNum.Common.Extend; 
namespaceAsNum.MySecret.DB{ 
/// 
/// 
/// 
publicpartialclassSPs{ 
/// 
///发送消息 
/// 
/// 
///@Title消息标题 
///@Ctx消息内容 
///@UserID用户ID 
///@FromIP发消息的IP 
///@IntranetIP发消息的内网IP,用于扩展 
///@IsPublic是否公开 
///@Receiver消息接收者,表变量 
/// 
publicstaticSqlCommandSendMsg(Databasedb,stringtitle,stringctx,int?userID,long?fromIP,long?intranetIP,long?isPublic,Listreceiver){ 
if(db==null) 
thrownewArgumentNullException("db"); 
SqlCommandsc=newSqlCommand("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()); 
returnsc; 
} 
} 
}

可以看到产生的存储过程调用方法并没有获取到参数的默认值,这个是因为(http://msdn.microsoft.com/en-us/library/ms176074.aspx):

SQL Server only maintains default values for CLR objects in this catalog view;

不过,有牛人做出来了,前提是存储过程没有加密:

http://www.codeproject.com/KB/database/FindDefaultValueSPParams.aspx

#p#

在声明 Table / View / TableType 的时候:

CREATETYPETMessageReceiverASTABLE( 
SendTypeVARCHAR(10)NOTNULL, 
ReceiverNVARCHAR(128)NOTNULL
) 
GO 
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'消息接收人,相同SendType和Recever只能出现一次',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TYPE',@level1name=N'TMessageReceiver'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'消息发送类型EML(QQ,MSN留扩展)',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TYPE',@level1name=N'TMessageReceiver',@level2type=N'COLUMN',@level2name=N'SendType'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'接收地址,跟据SendType',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TYPE',@level1name=N'TMessageReceiver',@level2type=N'COLUMN',@level2name=N'Receiver'
GO 

————

CREATETABLELoginPolicy( 
PolicyIDINTIDENTITY(1,1)NOTNULL, 
UnfreezeTimeINTNOTNULLDEFAULT30, 
MaxFailedCountINTNOTNULLDEFAULT5, 
EnableLoginPolicyBITDEFAULT1,--0:不启用,1:启用 
CreateTimeDATETIMENOTNULLDEFAULTGETDATE(), 
CreatorNVARCHAR(30), 
CONSTRAINTLoginPolicy_PKPRIMARYKEY(PolicyID) 
) 
GO 
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'登陆策略',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'TABLE',@level1name=N'LoginPolicy'
EXECsys.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'PolicyID'
EXECsys.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'UnfreezeTime'
EXECsys.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'MaxFailedCount'
EXECsys.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 

声明存储过程:

CREATEPROCEDURESendMsg 
@TitleNVARCHAR(100), 
@CtxNVARCHAR(500), 
@UserIDINT, 
@FromIPBIGINT, 
@IntranetIPBIGINT, 
@IsPublicBIGINT, 
@ReceiverTMessageReceiverREADONLY 
AS
BEGIN
--表变量,用以存储新增的主表ID 
DECLARE@TASTABLE(IDINT) 
BEGINTRANNewMsg 
BEGINTRY 
--写入主表 
INSERTINTO[Message] 
(Title,Ctx,UserID,FromIP,IntranetIP,IsPublic) 
OUTPUTINSERTED.MessageIDINTO@T 
VALUES
(@Title,@Ctx,@UserID,@FromIP,@IntranetIP,@IsPublic) 
--取出新增数据的ID 
DECLARE@MessageIDINT
SELECTTOP1@MessageID=IDFROM@T 
--写子表,这里要改动一下,相同的只保留一条 
INSERTINTOMessageReceiver 
(MessageID,SendType,Receiver) 
SELECT
@MessageID,R.SendType,R.Receiver 
FROM
@ReceiverR 
ENDTRY 
BEGINCATCH 
ROLLBACKTRANNewMsg 
RETURN2--DatabaseError 
ENDCATCH 
COMMITTRANNewMsg 
RETURN0 
END
GO 
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'发送消息',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'PROCEDURE',@level1name=N'SendMsg'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'消息标题',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'PROCEDURE',@level1name=N'SendMsg',@level2type=N'PARAMETER',@level2name='@Title'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'消息内容',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'PROCEDURE',@level1name=N'SendMsg',@level2type=N'PARAMETER',@level2name='@Ctx'
EXECsys.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'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'发消息的IP',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'PROCEDURE',@level1name=N'SendMsg',@level2type=N'PARAMETER',@level2name='@FromIP'
EXECsys.sp_addextendedproperty@name=N'MS_Description',@value=N'发消息的内网IP,用于扩展',@level0type=N'SCHEMA',@level0name=N'dbo',@level1type=N'PROCEDURE',@level1name=N'SendMsg',@level2type=N'PARAMETER',@level2name='@IntranetIP'
EXECsys.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'
EXECsys.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 会做为注释写到生成的代码里。

实现T4模板就很简单了,就是把下面几个SQL的结果取出来,在自由的组合一下:

WITHEntitiesAS( 
SELECT
Table_catalogAS[Database] 
,table_schema[Owner] 
,table_name[Name] 
,'Table'[Type] 
,OBJECT_ID(TABLE_SCHEMA+'.'+TABLE_NAME)ID 
FROM
INFORMATION_SCHEMA.TABLES 
WHERE
TABLE_TYPE='BASETABLE'
UNIONALL
SELECT
Table_catalog 
,table_schema 
,table_name 
,'View'
,OBJECT_ID(TABLE_SCHEMA+'.'+TABLE_NAME)ID 
FROM
information_schema.views 
UNIONALL
SELECT
DB_NAME() 
,SCHEMA_NAME(schema_id) 
,name
,'TableType'
,user_type_id 
FROM
sys.table_types 
) 
SELECT
E.* 
,P.valueAS[DESC] 
FROM
EntitiesE 
LEFTJOINsys.extended_propertiesPONE.ID=P.major_idANDP.minor_id=0

Table / View / TableType

字段:

SELECT
DB_NAME()AS[Database] 
,COALESCE(SCHEMA_NAME(T.Schema_id),OBJECT_SCHEMA_NAME(C.object_id))ASOwner 
,COALESCE(T.Name,OBJECT_NAME(c.OBJECT_ID))ASParent 
,C.column_idASOrdinalPosition 
,C.nameASName
,TYPE_NAME(c.user_type_id)ASDataType 
,D.definitionASDefaultSetting 
,C.is_nullableASIsNullable 
,C.max_lengthASMaxLength 
,COLUMNPROPERTY(C.OBJECT_ID,C.Name,'PRECISION')AS[Precision]--用于判断NVARCHAR实际长度的 
--,C.Object_id,P.major_id,P.minor_id 
,P.ValueAS[DESC] 
FROM
sys.columnsC 
LEFTJOINsys.table_typesTONT.type_table_object_id=C.object_ID 
LEFTJOINsys.default_constraintsDONC.object_id=D.parent_object_idANDD.parent_column_id=C.Column_id 
LEFTJOINsys.extended_propertiesPON(P.major_id=C.OBJECT_IDORP.major_id=T.User_type_id)ANDP.minor_id=C.COLUMN_IDANDP.name='MS_Description'
WHERE
COALESCE(SCHEMA_NAME(T.Schema_id),OBJECT_SCHEMA_NAME(C.object_id))='dbo'

存储过程:

SELECT
SCHEMA_NAME(P.schema_id)ASOwner 
,P.Name
,PP.ValueAS[Desc] 
FROM
sys.proceduresP 
LEFTJOINsys.extended_propertiesPPONP.object_id=PP.major_idANDPP.minor_id=0

存储过程的参数:

SELECT
SCHEMA_NAME(P.[schema_id])AS[Schema] 
,P.NameAS[Proc] 
,PA.Name
,(SELECTCOUNT(1)FROMsys.table_typesWHEREuser_type_id=PA.user_type_id)ASIsTableType 
,TYPE_NAME(PA.user_type_id)ASDataType 
,PA.max_lengthASMaxLength 
,COLUMNPROPERTY(PA.OBJECT_ID,PA.Name,'PRECISION')AS[Precision]--用于判断NVARCHAR实际长度的 
,PA.is_outputASIsOutput 
,PP.valueAS[Desc] 
FROM
sys.proceduresP 
INNERJOINsys.parametersPAONP.object_id=PA.object_id 
LEFTJOINsys.extended_propertiesPPONPA.object_id=PP.major_idANDPA.parameter_id=PP.minor_id 
ORDERBY
PA.Object_id,PA.Parameter_id

源代码包:http://files.cnblogs.com/xling/ExecuteT4.7z

这个包里有一个如何用程序运行T4模板的示例,加这个是因为同事问我如何用代码去执行T4模板,昨晚K歌完到家12点半(跟经理道别),洗完澡后,我从以前写的代码里翻出来的,稍稍做了点修改。还有一份数据字典导出模板.

另外需要注意的是:

1, Microsoft.VisualStudio.TextTemplating.dll 这个 DLL,如果你装的是VS2010 就默认装了这个。如果是VS2008, 需要从:Visual Studio 2008 SDK 1.1 里找

2, 如图

遗留的问题:

有些默认值没有处理好,比如对DateTime字段只处理了这种情况: GETDATE() 或 ‘2011-01-01’ , 其它的我暂时没有遇到,没有做处理。

存储过程的 TableType 参数,我简单的映射为 DbType.Object ,还没有验证这样是否可行。

后面我会把这些都修正的,如果你有兴趣的话,可以留意。

原文链接:https://77isp.com/post/6896.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。