一、原理与环境
在生成数据表的实体类时,利用自定义特性,给它打上表及字段的特性,然后使用反射原理,将自定义特性拼接成增、删、改、查对应的SQL,即可完成一个简单的ORM。
本示例的执行环境:
1)数据库:SQL Server。(可根据自己的需要,建立不同的数据库工厂。)
2)数据表:需使用自增类型(identity)作为数据表的主键。主键名字可以随便起,如ID。
3)实体类:实体类需提供无参构造函数。
二、演示数据表
Person表,包含主键(ID)、姓名(Name)、年龄(Age)、性别(Gender)。
CREATE TABLE [dbo].[Person]( [ID] [BIGINT] IDENTITY(1,1) NOT NULL, [Name] [NVARCHAR](50) NULL, [Age] [INT] NULL, [Gender] [NVARCHAR](10) NULL, CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
二、自定义特性
定义两个自定义特性:
2.1、DataTableAttribute
此为数据表特性,包含表名(TableName)、主键(Key)。
[Serializable] public class DataTableAttribute : Attribute { /// <summary> /// 数据表 /// </summary> public string TableName { get; } /// <summary> /// 主键 /// </summary> public string Key { get; } /// <summary> /// 构造函数 /// </summary> /// <param name="tableName"></param> /// <param name="key"></param> public DataTableAttribute(string tableName, string key) { TableName = tableName; Key = key; } }
2.2、DataFieldAttribute
此为字段特性,包含字段名(FieldName)、字段类型(FieldType)、长度(Length)、是否自增(IsIdentity)。
[Serializable] public class DataFieldAttribute : Attribute { public string FieldName { get; set; } public string FieldType { get; set; } public int Length { get; set; } public bool IsIdentity { get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="fieldName">字段名</param> /// <param name="fieldType">字段类型</param> /// <param name="length">长度</param> /// <param name="isIdentity">是否自增长</param> public DataFieldAttribute(string fieldName, string fieldType, int length, bool isIdentity) { FieldName = fieldName; FieldType = fieldType; Length = length; IsIdentity = isIdentity; } /// <summary> /// 构造函数 /// </summary> /// <param name="fieldName"></param> /// <param name="length"></param> /// <param name="isIdentity"></param> public DataFieldAttribute(string fieldName, string fieldType, int length) : this(fieldName, fieldType, length, false) { } /// <summary> /// 构造函数 /// </summary> /// <param name="fieldName"></param> /// <param name="fieldtype"></param> public DataFieldAttribute(string fieldName, string fieldType) : this(fieldName, fieldType, 0, false) { } /// <summary> /// 构造函数 /// </summary> /// <param name="fieldName"></param> /// <param name="isIdentity"></param> public DataFieldAttribute(string fieldName, bool isIdentity) : this(fieldName, "", 0, isIdentity) { } /// <summary> /// 构造函数 /// </summary> /// <param name="fieldName"></param> public DataFieldAttribute(string fieldName) : this(fieldName, false) { } }
三、生成实体类
3.1、实体类样式
依照前面的规划,Person表需要生成下面这个样子:
using System; using System.Collections.Generic; using System.Text; using LinkTo.ORM.CustomAttribute; namespace LinkTo.ORM.Model { [DataTable("Person","ID")] [Serializable] public class Person { public Person() { } [DataField("ID","bigint",19,true)] public long? ID {get; set;} [DataField("Name","nvarchar",50,false)] public string Name {get; set;} [DataField("Age","int",10,false)] public int? Age {get; set;} [DataField("Gender","nvarchar",10,false)] public string Gender {get; set;} } }
3.2、使用T4模板生成实体类
3.2.1、T4Code文件夹的文本模板
<#@ assembly name="System.Core" #> <#@ assembly name="System.Data" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Data"#> <#@ import namespace="System.Data.SqlClient"#> <#+ #region T4Code /// <summary> /// 数据库架构接口 /// </summary> public interface IDBSchema : IDisposable { List<string> GetTableList(); DataTable GetTableMetadata(string tableName); } /// <summary> /// 数据库架构工厂 /// </summary> public class DBSchemaFactory { static readonly string DatabaseType = "SqlServer"; public static IDBSchema GetDBSchema() { IDBSchema dbSchema; switch (DatabaseType) { case "SqlServer": { dbSchema =new SqlServerSchema(); break; } default: { throw new ArgumentException("The input argument of DatabaseType is invalid."); } } return dbSchema; } } /// <summary> /// SqlServer /// </summary> public class SqlServerSchema : IDBSchema { public string ConnectionString = "Server=.;Database=Test;Uid=sa;Pwd=********;"; public SqlConnection conn; public SqlServerSchema() { conn = new SqlConnection(ConnectionString); conn.Open(); } public List<string> GetTableList() { List<string> list = new List<string>(); string commandText = "SELECT NAME TABLE_NAME FROM SYSOBJECTS WHERE XTYPE='U' ORDER BY NAME"; using(SqlCommand cmd = new SqlCommand(commandText, conn)) { using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { while (dr.Read()) { list.Add(dr["TABLE_NAME"].ToString()); } } } return list; } public DataTable GetTableMetadata(string tableName) { string commandText=string.Format ( "SELECT A.NAME TABLE_NAME,B.NAME FIELD_NAME,C.NAME DATATYPE,ISNULL(B.PREC,0) LENGTH, "+ "CONVERT(BIT,CASE WHEN NOT F.ID IS NULL THEN 1 ELSE 0 END) ISKEY, "+ "CONVERT(BIT,CASE WHEN COLUMNPROPERTY(B.ID,B.NAME,'ISIDENTITY') = 1 THEN 1 ELSE 0 END) AS ISIDENTITY, "+ "CONVERT(BIT,B.ISNULLABLE) ISNULLABLE "+ "FROM SYSOBJECTS A INNER JOIN SYSCOLUMNS B ON A.ID=B.ID INNER JOIN SYSTYPES C ON B.XTYPE=C.XUSERTYPE "+ "LEFT JOIN SYSOBJECTS D ON B.ID=D.PARENT_OBJ AND D.XTYPE='PK' "+ "LEFT JOIN SYSINDEXES E ON B.ID=E.ID AND D.NAME=E.NAME "+ "LEFT JOIN SYSINDEXKEYS F ON B.ID=F.ID AND B.COLID=F.COLID AND E.INDID=F.INDID "+ "WHERE A.XTYPE='U' AND A.NAME='{0}' "+ "ORDER BY A.NAME,B.COLORDER", tableName ); using(SqlCommand cmd = new SqlCommand(commandText, conn)) { SqlDataAdapter da = new SqlDataAdapter(cmd); DataSet ds = new DataSet(); da.Fill(ds,"Schema"); return ds.Tables[0]; } } public void Dispose() { if (conn != null) { conn.Close(); } } } #endregion #>