一、原理与环境
在生成数据表的实体类时,利用自定义特性,给它打上表及字段的特性,然后使用反射原理,将自定义特性拼接成增、删、改、查对应的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
#>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
<#+
// T4 Template Block manager for handling multiple file outputs more easily.
// Copyright (c) Microsoft Corporation.All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (MS-PL)
// Manager class records the various blocks so it can split them up
class Manager
{
public struct Block
{
public string Name;
public int Start, Length;
}
public List<Block> blocks = new List<Block>();
public Block currentBlock;
public Block footerBlock = new Block();
public Block headerBlock = new Block();
public ITextTemplatingEngineHost host;
public ManagementStrategy strategy;
public StringBuilder template;
public string OutputPath { get; set; }
public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader)
{
this.host = host;
this.template = template;
OutputPath = string.Empty;
strategy = ManagementStrategy.Create(host);
}
public void StartBlock(string name)
{
currentBlock = new Block { Name = name, Start = template.Length };
}
public void StartFooter()
{
footerBlock.Start = template.Length;
}
public void EndFooter()
{
footerBlock.Length = template.Length - footerBlock.Start;
}
public void StartHeader()
{
headerBlock.Start = template.Length;
}
public void EndHeader()
{
headerBlock.Length = template.Length - headerBlock.Start;
}
public void EndBlock()
{
currentBlock.Length = template.Length - currentBlock.Start;
blocks.Add(currentBlock);
}
public void Process(bool split)
{
string header = template.ToString(headerBlock.Start, headerBlock.Length);
string footer = template.ToString(footerBlock.Start, footerBlock.Length);
blocks.Reverse();
foreach(Block block in blocks) {
string fileName = Path.Combine(OutputPath, block.Name);
if (split) {
string content = header + template.ToString(block.Start, block.Length) + footer;
strategy.CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
} else {
strategy.DeleteFile(fileName);
}
}
}
}
class ManagementStrategy
{
internal static ManagementStrategy Create(ITextTemplatingEngineHost host)
{
return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
}
internal ManagementStrategy(ITextTemplatingEngineHost host) { }
internal virtual void CreateFile(string fileName, string content)
{
File.WriteAllText(fileName, content);
}
internal virtual void DeleteFile(string fileName)
{
if (File.Exists(fileName))
File.Delete(fileName);
}
}
class VSManagementStrategy : ManagementStrategy
{
private EnvDTE.ProjectItem templateProjectItem;
internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host)
{
IServiceProvider hostServiceProvider = (IServiceProvider)host;
if (hostServiceProvider == null)
throw new ArgumentNullException("Could not obtain hostServiceProvider");
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
if (dte == null)
throw new ArgumentNullException("Could not obtain DTE from host");
templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
}
internal override void CreateFile(string fileName, string content)
{
base.CreateFile(fileName, content);
((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
}
internal override void DeleteFile(string fileName)
{
((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
}
private void FindAndDeleteFile(string fileName)
{
foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
{
if (projectItem.get_FileNames(0) == fileName)
{
projectItem.Delete();
return;
}
}
}
}
#>
DBSchema.ttinclude主要实现了数据库工厂的功能。注:请将数据库连接字符串改成您自己的。
MultiDocument.ttinclude主要实现了多文档的功能。
3.2.2、生成实体类的文本模板
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="T4Code/DBSchema.ttinclude"#>
<#@ include file="T4Code/MultiDocument.ttinclude"#>
<# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<#
//System.Diagnostics.Debugger.Launch();//调试
var dbSchema = DBSchemaFactory.GetDBSchema();
List<string> tableList = dbSchema.GetTableList();
foreach (string tableName in tableList)
{
manager.StartBlock(tableName+".cs");
DataTable table = dbSchema.GetTableMetadata(tableName);
//获取主键
string strKey = string.Empty;
foreach (DataRow dataRow in table.Rows)
{
if ((bool)dataRow["ISKEY"] == true)
{
strKey = dataRow["FIELD_NAME"].ToString();
break;
}
}
#>
//-------------------------------------------------------------------------------
// 此代码由T4模板MultModelAuto自动生成
// 生成时间 <#= DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") #>
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//-------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using LinkTo.ORM.CustomAttribute;
namespace LinkTo.ORM.Model
{
[DataTable("<#= tableName #>","<#= strKey #>")]
[Serializable]
public class <#= tableName #>
{
public <#= tableName #>() { }
<#
foreach (DataRow dataRow in table.Rows)
{
//获取数据类型
string dbDataType = dataRow["DATATYPE"].ToString();
string dataType = string.Empty;
switch (dbDataType)
{
case "decimal":
case "numeric":
case "money":
case "smallmoney":
dataType = "decimal?";
break;
case "char":
case "nchar":
case "varchar":
case "nvarchar":
case "text":
case "ntext":
dataType = "string";
break;
case "uniqueidentifier":
dataType = "Guid?";
break;
case "bit":
dataType = "bool?";
break;
case "real":
dataType = "Single?";
break;
case "bigint":
dataType = "long?";
break;
case "int":
dataType = "int?";
break;
case "tinyint":
case "smallint":
dataType = "short?";
break;
case "float":
dataType = "float?";
break;
case "date":
case "datetime":
case "datetime2":
case "smalldatetime":
dataType = "DateTime?";
break;
case "datetimeoffset ":
dataType = "DateTimeOffset?";
break;
case "timeSpan ":
dataType = "TimeSpan?";
break;
case "image":
case "binary":
case "varbinary":
dataType = "byte[]";
break;
default:
break;
}
#>
[DataField("<#= dataRow["FIELD_NAME"].ToString() #>","<#= dataRow["DATATYPE"].ToString() #>",<#= dataRow["LENGTH"].ToString() #>,<#= dataRow["ISIDENTITY"].ToString().ToLower() #>)]
public <#= dataType #> <#= dataRow["FIELD_NAME"].ToString() #> {get; set;}
<#
}
#>
}
}
<#
manager.EndBlock();
}
dbSchema.Dispose();
manager.Process(true);
#>
注:由于ORM拼接SQL时使用的是表特性及字段特性,可以看出表特性上使用的表名、字段特性上使用的字段名,都是与数据库一致的。有了这个保障,数据表生成实体类的时候,类名是可以更改的,因为我只需要保证表特性与数据库一致即可。举个例子,我有个数据表Person_A,在生成实体类时,类名可以生成为Class PersonA {...},但是表特性依然是[DataTable("Person_A","...")]。相同的原理,属性名也是可以更改的。
四、ORM实现
数据表的CURD,主要是通过反射来实现SQL拼接,实现如下:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using LinkTo.ORM.CustomAttribute;
namespace LinkTo.ORM
{
public static class DBHelper
{
public static readonly string ConnectionString = "Server=.;Database=Test;Uid=sa;Pwd=********;";
private static readonly Hashtable _HashTableName = new Hashtable(); //表名缓存
private static readonly Hashtable _HashKey = new Hashtable(); //主键缓存
/// <summary>
/// 数据库连接
/// </summary>
/// <returns></returns>
public static SqlConnection GetConnection()
{
SqlConnection conn = new SqlConnection(ConnectionString);
return conn;
}
/// <summary>
/// 新增
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public static int Insert<TEntity>(TEntity entity) where TEntity : class
{
string strTableName = ""; //表名
string strInsertSQL = "INSERT INTO {0} ({1}) VALUES ({2})"; //SQL拼接语句
//获取表名
strTableName = GetTableName(entity);
//获取字段列表及值列表
StringBuilder strFields = new StringBuilder();
StringBuilder strValues = new StringBuilder();
List<SqlParameter> paraList = new List<SqlParameter>();
PropertyInfo[] infos = entity.GetType().GetProperties();
DataFieldAttribute dfAttr = null;
object[] dfAttrs;
int i = 0;
foreach (PropertyInfo info in infos)
{
dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
if (dfAttrs.Length > 0)
{
dfAttr = dfAttrs[0] as DataFieldAttribute;
if (dfAttr is DataFieldAttribute)
{
//自增字段不作处理
if (dfAttr.IsIdentity) continue;
strFields.Append(i > 0 ? "," + dfAttr.FieldName : dfAttr.FieldName);
strValues.Append(i > 0 ? "," + "@" + dfAttr.FieldName : "@" + dfAttr.FieldName);
i++;
paraList.Add(new SqlParameter("@" + dfAttr.FieldName, info.GetValue(entity, null)));
}
}
}
//格式化SQL拼接语句
string[] args = new string[] { strTableName, strFields.ToString(), strValues.ToString() };
strInsertSQL = string.Format(strInsertSQL, args);
//执行结果
int result = 0;
try
{
using (SqlConnection conn = GetConnection())
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = strInsertSQL;
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
if (paraList != null)
{
foreach (SqlParameter param in paraList)
{
cmd.Parameters.Add(param);
}
}
result = cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
//返回影响行数
return result;
}
/// <summary>
/// 删除
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="condition"></param>
/// <returns></returns>
public static int Delete<TEntity>(string condition) where TEntity : class, new()
{
string strTableName = ""; //表名
string strDeleteSQL = "DELETE FROM {0} WHERE {1}"; //SQL拼接语句
//获取表名
strTableName = GetTableName(new TEntity());
//格式化SQL拼接语句
string[] args = new string[] { strTableName, condition };
strDeleteSQL = string.Format(strDeleteSQL, args);
//执行结果
int result = 0;
try
{
using (SqlConnection conn = GetConnection())
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = strDeleteSQL;
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
result = cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
//返回影响行数
return result;
}
/// <summary>
/// 更新
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public static int Update<TEntity>(TEntity entity) where TEntity : class
{
string strTableName = ""; //表名
string strUpdateSQL = "UPDATE {0} SET {1} WHERE {2}"; //SQL拼接语句
string strKey = ""; //主键
string strWhere = ""; //条件
//获取表名及主键
strTableName = GetTableName(entity);
strKey = GetKey(entity);
//获取更新列表
StringBuilder strSET = new StringBuilder();
List<SqlParameter> paraList = new List<SqlParameter>();
PropertyInfo[] infos = entity.GetType().GetProperties();
DataFieldAttribute dfAttr = null;
object[] dfAttrs;
int i = 0;
foreach (PropertyInfo info in infos)
{
dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
if (dfAttrs.Length > 0)
{
dfAttr = dfAttrs[0] as DataFieldAttribute;
if (dfAttr is DataFieldAttribute)
{
//条件处理
if (dfAttr.FieldName == strKey)
{
strWhere = strKey + "=" + info.GetValue(entity, null);
}
//自增字段不作处理
if (dfAttr.IsIdentity) continue;
strSET.Append(i > 0 ? "," + dfAttr.FieldName + "=@" + dfAttr.FieldName : dfAttr.FieldName + "=@" + dfAttr.FieldName);
i++;
paraList.Add(new SqlParameter("@" + dfAttr.FieldName, info.GetValue(entity, null)));
}
}
}
//格式化SQL拼接语句
string[] args = new string[] { strTableName, strSET.ToString(), strWhere };
strUpdateSQL = string.Format(strUpdateSQL, args);
//执行结果
int result = 0;
try
{
using (SqlConnection conn = GetConnection())
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = strUpdateSQL;
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
if (paraList != null)
{
foreach (SqlParameter param in paraList)
{
cmd.Parameters.Add(param);
}
}
result = cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
//返回影响行数
return result;
}
/// <summary>
/// 查询
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="condition"></param>
/// <returns></returns>
public static List<TEntity> Query<TEntity>(string condition) where TEntity : class, new()
{
string strTableName = ""; //表名
string strSelectSQL = "SELECT * FROM {0} WHERE {1}"; //SQL拼接语句
List<TEntity> list = new List<TEntity>(); //实体列表
//获取表名
strTableName = GetTableName(new TEntity());
//格式化SQL拼接语句
string[] args = new string[] { strTableName, condition };
strSelectSQL = string.Format(strSelectSQL, args);
//获取实体列表
PropertyInfo[] infos = typeof(TEntity).GetProperties();
DataFieldAttribute dfAttr = null;
object[] dfAttrs;
try
{
using (SqlConnection conn = GetConnection())
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strSelectSQL, conn))
{
using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (dr.Read())
{
TEntity entity = new TEntity();
foreach (PropertyInfo info in infos)
{
dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
if (dfAttrs.Length > 0)
{
dfAttr = dfAttrs[0] as DataFieldAttribute;
if (dfAttr is DataFieldAttribute)
{
info.SetValue(entity, dr[dfAttr.FieldName]);
}
}
}
list.Add(entity);
}
}
}
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
//返回实体列表
return list;
}
/// <summary>
/// 根据实体返回表名
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static string GetTableName<TEntity>(TEntity entity) where TEntity : class
{
Type entityType = entity.GetType();
string strTableName = Convert.ToString(_HashTableName[entityType.FullName]);
if (strTableName == "")
{
if (entityType.GetCustomAttributes(typeof(DataTableAttribute), false)[0] is DataTableAttribute dtAttr)
{
strTableName = dtAttr.TableName;
}
else
{
throw new Exception(entityType.ToString() + "未设置DataTable特性。");
}
_HashTableName[entityType.FullName] = strTableName;
}
return strTableName;
}
/// <summary>
/// 根据实体返回主键
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static string GetKey<TEntity>(TEntity entity) where TEntity : class
{
Type entityType = entity.GetType();
string strKey = Convert.ToString(_HashKey[entityType.FullName]);
if (strKey == "")
{
if (entityType.GetCustomAttributes(typeof(DataTableAttribute), false)[0] is DataTableAttribute dtAttr)
{
strKey = dtAttr.Key;
}
else
{
throw new Exception(entityType.ToString() + "未设置DataTable特性。");
}
_HashKey[entityType.FullName] = strKey;
}
return strKey;
}
}
}
五、运行测试
新建一个控制台程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LinkTo.ORM.Model;
namespace LinkTo.ORM.Client
{
class Program
{
static void Main()
{
//新增
Person insertPerson = new Person
{
Name = "Hello",
Age = 18,
Gender = "male"
};
int insertResult = DBHelper.Insert(insertPerson);
Console.WriteLine($"共新增了 {insertResult} 条记录。");
//更新
List<Person> updateList = DBHelper.Query<Person>("Name='Hello'");
int updateResult = 0;
if (updateList.Count > 0)
{
foreach (var item in updateList)
{
Person updatePerson = item;
updatePerson.Age = 19;
updateResult += DBHelper.Update(updatePerson);
}
}
Console.WriteLine($"共更新了 {updateResult} 条记录。");
//查询
List<Person> selectList = DBHelper.Query<Person>("Name='Hello'");
if (selectList.Count > 0)
{
foreach (var item in selectList)
{
Console.WriteLine("person.Name = " + item.Name);
Console.WriteLine("person.Age = " + item.Age);
Console.WriteLine("person.Gender = " + item.Gender);
}
}
//删除
int deleteResult = DBHelper.Delete<Person>("Name='Hello'");
Console.WriteLine($"共删除了 {deleteResult} 条记录。");
Console.Read();
}
}
}
运行结果如下:
参考自:
MiniORM
https://blog.csdn.net/m0_37224390/article/details/81134550
来源:oschina
链接:https://my.oschina.net/u/4255339/blog/4254085