.NET实现之(简易ORM)
ORM模型在现在的项目开发中用的还是比较多的,他的好坏我们就不讨论了,已学习为主吧;
ORM模型的大概思想就是实体与我们库中的表的对应关系,一个实体就好比表中的一行数据,将实体的操作视为对表的操作,如增、删、该、查就是对表的同样操作,我们只需要简单的封装一下就可以省很多的重复劳动,通过ORM模型对我们的代码的简洁程度还是有目共睹的,我们将对数据库的操作通过ORM转换后执行,前者我们省掉了很多复杂的SQL组装,后者其实是一样的只不过将SQL的组装通过ORM将MODEL的一些属性通过反射的方式获取然后拼接SQL去执行,在项目的数据库变更,很容易就切换了上下文的数据库访问代码,将诸如SqlConnection、SqlCommand之类的针对性的数据访问代码,封装起来实现简单灵活的访问,ORM模型确实为我们的开发提高了效率,下面我就用我写的简单的ORM模型来讲解一下ORM模型的大概实现方式和基本原理;[王清培版权所有,转载请给出署名]
1:
这是我测试数据库中的一张tb_user表,其中id是自动增长主键,下面我们就用这张表来讲解我是怎么通过ORM来实现对这张表的增、删、该、查的;
2:
这是我项目中的与上图对应的MODEL,我用两个特性标记出MODEL中与表对应的信息,表名、列的主键、是否自增长;用这些特性的目的就是在ORM模型的核心部分拼接SQL代码时,在做删除、更新时知道那一列是对应数据库中的主键,在做增加时我要知道那些列是自增长的等等;
3:
这是我的项目中的具体类型信息,本人已上传全部源码,为了便于大家学习我就简单的介绍一下这些类型具体是什么意思;
上图是两个项目,第一个CommandLib.Test是做测试使用的,里面有一个TB_user类型,它对应我库中的一张表;X.CommandLib是ORM模型的实现;我就从Data目录往下介绍一下,ColumnAttribute是MODEL的列特性信息,用来标识列是否是主键、自增长;DataSourceType是不同数据源类型枚举如:SqlServer、Oracle、OLEDB等;IDataParameterFactory是所有数据源操作时的IDataParameter接口的工厂,这样就可以很方便的切换不同数据源的操作参数类型;IDataSourceType是一个接口类型,这个接口的作用就是用来说明,不管是哪种数据源的操作对象都必须实现这一组规范,这样可以严格控制我们上层在调用的时候很顺手方便,在这个接口里面基本上就是对数据库的常用操作了;IDataSourceTypeFactory是一个IDataSourceType接口的工厂,这个工厂通过读取配置文件来确定当前程序的上下文使用什么类型的数据源操作对象;OledbSource是实现了IDataSourceType接口的Oledb数据源操作类;OracleSource也是实现了IDataSourceType接口的ORACLE数据源操作类;ORMHelper是核心ORM模型的实现,这里面就是所有的MODEL与SQL代码之间的转换实现;SQLServerSource也是实现了IDataSourceType接口的SQLServer数据源的实现;TableActionType是ORM模型在操作时需要确定当前对表的操作类型(增、删、改、查);TableAttibute是MODEL中的表信息特性,用来确定当前的MODEL是对应于库中的哪一站表;所有的类型就介绍完了,下面给出总体的结构图,以便读者对上图的结构很清晰明了;[王清培版权所有,转载请给出署名]
4:
下面我就简单讲解一下ORMHelper.cs实现细节,如果有兴趣可以下载源码学习;[王清培版权所有,转载请给出署名]
5:
ORMHelper.cs里面只有一个字段和一个只读属性,作用就是用来保存Model首次使用时,保存当前Model中的所有的主键信息;以便下次使用时我好方便的读取;6:
这是本人测试的例子,1.统一数据源封装实现,我们通过IDataSourceTypeFactory工厂获取当前上下问的数据源访问对象,其实我们上面也讲了,IDataSourceType是对数据源访问的封装实现(如:SqlConnection、SqlCommand、SqlParameter)已一个简单的对象呈现使用;
2.通过ORMHelper实现用MODEL插入数据,插入方法是一个泛型方法,大概的实现思路是这样的,我们通过反射读取传进来的Model实例中的各个特性信息,如表名称、列主键等;然后拼接SQL语句,执行,其实增、删、改、查都是同一个道理,在具体的拼接SQL的过程可能稍有不同;本人贴出ORMHelper.cs全部源码,以供大家学习使用;
/*---------------------------------------------------------------- * 作者:王清培 * 时间:2010-10-29 * 描述:ORM模型的实现类,该类的主要目的实现方便的数据库表的增、删、该、查与实体之间的映射关系 * 将对表的操作利用统一的Model实体对象来操作 * ----------------------------------------------------------------*/ using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.OracleClient; using System.Data.OleDb; using System.Reflection; namespace W.Data { /// <summary> /// ORM模型静态帮助类 /// </summary> public static class ORMHelper { #region 静态字段 /// <summary> /// Model主键字典缓存 /// </summary> private static Dictionary<string, List<string>> _pkcache = new Dictionary<string, List<string>>(); #endregion #region 静态属性 /// <summary> /// 获取,Model主键字段缓存 /// </summary> public static Dictionary<string, List<string>> PKCache { get { return _pkcache; } } #endregion #region 静态方法 /// <summary> /// 根据数据行构造泛型实体 /// </summary> /// <typeparam name="T">返回的实体类型</typeparam> /// <param name="row">数据行</param> /// <returns>已赋值的Model实体对象</returns> public static T RowToModel<T>(DataRow row) where T : new() { T result = new T();//实例化要返回的泛型实体 Type resulttype = typeof(T);//获取实体的Type类型信息 PropertyInfo[] propertycoll = resulttype.GetProperties();//获取所有属性列表 foreach (var item in propertycoll) { //如果当前ROW中不存在于该MODEL实体对应的列名称 if (!row.Table.Columns.Contains(item.Name)) continue; //判断属性是否可以为空类型 //字符串类型 if (item.PropertyType == typeof(string)) item.SetValue(result, row[item.Name], null); //整型(不可空) else if (item.PropertyType == typeof(int)) item.SetValue(result, row[item.Name] == null ? 0 : row[item.Name], null); //整型(可空) else if (item.PropertyType == typeof(int?)) item.SetValue(result, row[item.Name], null); //长整型(不可空) else if (item.PropertyType == typeof(long)) item.SetValue(result, row[item.Name] == null ? 0 : row[item.Name], null); //长整型(可空) else if (item.PropertyType == typeof(long?)) item.SetValue(result, row[item.Name], null); //布尔值(不可空) else if (item.PropertyType == typeof(bool)) item.SetValue(result, row[item.Name] == null ? false : row[item.Name], null); //布尔值(可空) else if (item.PropertyType == typeof(bool?)) item.SetValue(result, row[item.Name], null); //时间(不可空) else if (item.PropertyType == typeof(DateTime)) { DateTime outtime; string datetime = row[item.Name] != null ? row[item.Name].ToString() : string.Empty; if (datetime.Length > 0) { DateTime.TryParse(datetime, out outtime); item.SetValue(result, outtime, null); } else { item.SetValue(result, new DateTime(), null); } } //时间(可空) else if (item.PropertyType == typeof(DateTime?)) { DateTime outtime; string datetime = row[item.Name] != null ? row[item.Name].ToString() : string.Empty; if (datetime.Length > 0) { DateTime.TryParse(datetime, out outtime); item.SetValue(result, outtime, null); } } //双精度(不可空) else if (item.PropertyType == typeof(double)) item.SetValue(result, row[item.Name] == null ? 0 : row[item.Name], null); //双精度(可空) else if (item.PropertyType == typeof(double)) item.SetValue(result, row[item.Name], null); //十进制实数(不可空) else if (item.PropertyType == typeof(decimal)) item.SetValue(result, row[item.Name] == null ? 0 : row[item.Name], null); //十进制实数(可空) else if (item.PropertyType == typeof(decimal)) item.SetValue(result, row[item.Name], null); } return result; } /// <summary> /// 根据数据表构造泛型实体集合 /// </summary> /// <typeparam name="T">返回的实体类型</typeparam> /// <param name="tb">数据表</param> /// <returns>已赋值Model实体对象集合</returns> public static List<T> TbToModelList<T>(DataTable tb) where T : new() { List<T> resultlist = new List<T>(tb.Rows.Count); foreach (DataRow tr in tb.Rows) { resultlist.Add(RowToModel<T>(tr)); } return resultlist; } /// <summary> /// 根据Model构造等价DataRow,该DataRow属于动态生成的DataTable架构 /// </summary> /// <typeparam name="T">待构造的实体类型</typeparam> /// <param name="t">实体对象</param> /// <returns>已构造的DataRow</returns> public static DataRow ModelToRow<T>(T t) where T : new() { List<T> list = new List<T>(); list.Add(t); return ModelListToTb<T>(list).Rows[0]; } /// <summary> /// 根据Model集合构造等价的DataTable /// </summary> /// <typeparam name="T">待构造的实体类型</typeparam> /// <param name="t">实体对象集合</param> /// <returns>已构造的DataTable</returns> public static DataTable ModelListToTb<T>(IList<T> t) where T : new() { DataTable resulttb = new DataTable(); Type type = typeof(T); //构造表的列 foreach (PropertyInfo info in type.GetProperties()) { resulttb.Columns.Add(info.Name, info.GetType()); } //赋值 foreach (T child in t) { DataRow row = resulttb.NewRow(); foreach (PropertyInfo info in type.GetProperties()) { row[info.Name] = info.GetValue(child, null); } resulttb.Rows.Add(row); } return resulttb; } /// <summary> /// 根据Model实体添加一条表记录, /// </summary> /// <typeparam name="T">泛型实体类型</typeparam> /// <param name="t">实例</param> /// <returns>插入是否成功1:成功,0失败</returns> public static int InsertModel<T>(T t) where T : new() { return InsertModel<T>(t, null); } /// <summary> /// 根据Model实体IDataSourceType对象添加一条表记录 /// </summary> /// <typeparam name="T">泛型实体类型</typeparam> /// <param name="t">实例</param> /// <param name="idatasource">IDataSourceType接口实例</param> /// <returns>插入是否成功1:成功,0失败</returns> public static int InsertModel<T>(T t, IDataSourceType idatasource) where T : new() { string sqlstring = string.Empty; List<IDataParameter> parame = (List<IDataParameter>)GetInsertModelParameterT_SQL<T>(t, out sqlstring); if (idatasource != null) return idatasource.ExecuteNonQuery(CommandType.Text, sqlstring, parame.ToArray()); return IDataSourceTypeFactory.Create().ExecuteNonQuery(CommandType.Text, sqlstring, parame.ToArray()); } /// <summary> /// 根据实体对象删除一条记录,该实体必须明确主键值才能删除记录; /// 如果该实体没有主键可自己编写SQL代码删除 /// </summary> /// <typeparam name="T">要删除的表对应的实体对象</typeparam> /// <param name="t">Model实体</param> /// <returns>删除是否成功;1成功,0失败</returns> public static int DeleteModelById<T>(T t) where T : new() { return DeleteModelById<T>(t, null); } /// <summary> /// 根据实体对象和IDataSourceType对象删除一条记录, /// 该实体必须明确主键值才能删除记录;如果该实体没有主键可自己编写SQL代码删除; /// </summary> /// <typeparam name="T">要删除的表对应的实体对象</typeparam> /// <param name="t">Model实体</param> /// <param name="idatasource">IDataSourceType数据源类型对象</param> /// <returns>删除是否成功;1成功,0失败</returns> public static int DeleteModelById<T>(T t, IDataSourceType idatasource) where T : new() { string sqlstring;//保存要执行的T-SQL语句 List<IDataParameter> paramlist = (List<IDataParameter>)GetDeleteModelParameterT_SQL<T>(t, out sqlstring);//获取利用Model删除时的语句和参数列表 if (idatasource != null) return IDataSourceTypeFactory.Create().ExecuteNonQuery(CommandType.Text, sqlstring, paramlist.ToArray()); return IDataSourceTypeFactory.Create().ExecuteNonQuery(CommandType.Text, sqlstring, paramlist.ToArray()); } /// <summary> /// 获取实体执行删除时的T-SQL语句 /// </summary> /// <typeparam name="T">泛型类型</typeparam> /// <param name="t">实体对象</param> /// <returns>生成的T-SQL语句</returns> public static string GetDeleteModelByIdT_SQL<T>(T t) where T : new() { Type type = typeof(T); string tbname = (type.GetCustomAttributes(typeof(W.Data.TableAttribute), false)[0] as W.Data.TableAttribute).TableName; StringBuilder sqlbuilder = new StringBuilder(); sqlbuilder.AppendFormat("DELETE FROM {0} WHERE 1=1", tbname); List<string> pklist = GetPKCache<T>(t);//获取该实体的主键 PropertyInfo[] property = type.GetProperties(); foreach (PropertyInfo info in property) { if ((info.GetValue(t, null) != null) && (pklist.Contains(info.Name))) { sqlbuilder.AppendFormat(" AND {0}='{1}'", info.Name, info.GetValue(t, null)); } } return sqlbuilder.ToString(); } /// <summary> /// 根据实体对象更新一条记录,该实体必须明确主键值才能更新记录 /// </summary> /// <typeparam name="T">要更新的表对应的实体对象类型</typeparam> /// <param name="t">Model实体</param> /// <returns>更新是否成功;1成功,0失败</returns> public static int UpdateModelById<T>(T t) where T : new() { return UpdateModelById<T>(t, null); } /// <summary> /// 根据实体对象和IDataSourceType对象更新一条记录 /// </summary> /// <typeparam name="T">要更新的表对应的实体对象类型</typeparam> /// <param name="t">Model实体</param> /// <param name="idatasource">IDataSourceType接口实例</param> /// <returns>更新是否成功;1成功,0失败</returns> public static int UpdateModelById<T>(T t, IDataSourceType idatasource) where T : new() { string sqlstring = string.Empty; List<IDataParameter> paramlist = (List<IDataParameter>)(GetUpdateModelParameterT_SQL<T>(t, out sqlstring)); if (idatasource != null) return idatasource.ExecuteNonQuery(CommandType.Text, sqlstring, paramlist.ToArray()); return W.Data.IDataSourceTypeFactory.Create().ExecuteNonQuery(CommandType.Text, sqlstring, paramlist.ToArray()); } /// <summary> /// 根据实体主键获取实体全部信息,如果没有主键返回最上面一条记录实体 /// </summary> /// <typeparam name="T">要构造的泛型实体类型</typeparam> /// <param name="t">Model类实体</param> /// <returns>填充完的实体对象</returns> public static T GetModelById<T>(T t) where T : new() { return GetModelById<T>(t, null); } /// <summary> /// 根据实体主键和IDataSourceType对象获取实体全部信息,如果没有主键返回最上面一条记录实体 /// </summary> /// <typeparam name="T">要构造的泛型实体类型</typeparam> /// <param name="t">Model类实体</param> /// <param name="idatasource">IDataSourceType数据源类型对象</param> /// <returns>填充完的实体对象</returns> public static T GetModelById<T>(T t, IDataSourceType idatasource) where T : new() { Type type = typeof(T); object[] objattribute = type.GetCustomAttributes(typeof(W.Data.TableAttribute), false); string tablename = string.Empty; if (objattribute.Length > 0) tablename = (objattribute[0] as W.Data.TableAttribute).TableName;//根据表名称特性类获取表名称 else tablename = type.Name;//否则默认以Model类的名称为表名 List<string> pklist = GetPKCache<T>(t);//获取主键集合 Dictionary<string, string> pkproperty = new Dictionary<string, string>(pklist.Count);//主键与主键的值字典 //构建主键的参数字典用作生成主键参数使用 foreach (var item in type.GetProperties()) { if (pklist.Contains(item.Name)) pkproperty.Add(item.Name, item.GetValue(t, null).ToString());//主键属性不能为空,如果为空直接抛出异常 } SqlParameter[] parameter = new SqlParameter[pklist.Count];//主键查询参数 //构建主键参数 for (int i = 0; i < pklist.Count; i++) { parameter[i] = new SqlParameter(string.Format("@{0}", pklist[i]), pkproperty[pklist[i]]);//自动生成主键与主键值的对应参数 } StringBuilder Appsql = new StringBuilder();//封装SQL语句 Appsql.AppendFormat("select top 1 *from {0} where 1=1", tablename);//如果不存在主键就默认选择第一条记录作为Model实体返回 for (int i = 0; i < pklist.Count; i++) { Appsql.AppendFormat(" and {0} = @{0}", pklist[i]);//动态填充所有主键属性列 } DataTable tb; if (idatasource == null) { tb = W.Data.IDataSourceTypeFactory.Create().ExecuteTable(CommandType.Text, Appsql.ToString(), parameter); } else { tb = idatasource.ExecuteTable(CommandType.Text, Appsql.ToString(), parameter); } if (tb.Rows.Count > 0) return RowToModel<T>(tb.Rows[0]);//生成实例 return default(T);//返回指定类型的初始化对象 } #endregion #region 静态内部方法 /// <summary> /// 私有方法,获取利用实体删除表记录时的SQL语句和参数列表 /// </summary> /// <typeparam name="T">要删除的表对应的实体类型</typeparam> /// <param name="t">Model实体</param> /// <param name="sql">需要保存生成后的SQL语句</param> /// <returns>参数列表</returns> private static IList<IDataParameter> GetDeleteModelParameterT_SQL<T>(T t, out string sql) where T : new() { StringBuilder sqlbuilder = new StringBuilder();//保存SQL语句 string tablename = string.Empty; Type type = typeof(T);//获取类型信息 object[] attribute = type.GetCustomAttributes(typeof(W.Data.TableAttribute), false); if (attribute.Length > 0) { tablename = (attribute[0] as W.Data.TableAttribute).TableName;//获取特性的表名 } else { //如果没有用特性标记为表名称,默认用实体的名称作为表的名称 tablename = type.Name; } List<string> Pklist = GetPKCache<T>(t);//获取该实体的所有主键列表 if (Pklist.Count <= 0) throw new Exception("来自X.CommandLib.dll.ORMHelper错误:没有主键的实体无法进行删除操作"); bool isand = true;//控制语句拼接的and条件 //根据主键生成SQL语句 foreach (string sub in Pklist) { if (isand) { sqlbuilder.AppendFormat("{0}=@{0}", sub); } else { sqlbuilder.AppendFormat("and {0}=@{0}", sub); } } sql = string.Format("DELETE FROM {0} WHERE {1}", tablename, sqlbuilder.ToString()); return GetActionParameter<T>(t, TableActionType.Delete); } /// <summary> /// 私有方法,获取利用实体插入表记录时的SQL语句和参数列表 /// </summary> /// <typeparam name="T">要插入的表对应的实体类型</typeparam> /// <param name="t">Model实体</param> /// <param name="sql">需要保存生成后的SQL语句</param> /// <returns>参数列表</returns> private static IList<IDataParameter> GetInsertModelParameterT_SQL<T>(T t, out string sql) where T : new() { string tablename = string.Empty;//要插入的表的名称 StringBuilder fieldbuilder = new StringBuilder();//要插入的字段列表 StringBuilder valuebuilder = new StringBuilder();//要插入的字段值 Type type = typeof(T); bool flag = true;//分割拼接的T-SQL语句 foreach (PropertyInfo info in type.GetProperties()) { object[] columbute = info.GetCustomAttributes(typeof(W.Data.ColumnAttribute), false); if (columbute.Length > 0) { if ((columbute[0] as W.Data.ColumnAttribute).IsIdentity) { continue;//如果是自增长就不处理这个属性 } } if (info.GetValue(t, null) != null) { if (flag) { fieldbuilder.Append(info.Name); valuebuilder.AppendFormat("@{0}", info.Name); flag = false; } else { fieldbuilder.AppendFormat(",{0}", info.Name); valuebuilder.AppendFormat(",@{0}", info.Name); } } } object[] tbattribute = type.GetCustomAttributes(typeof(W.Data.TableAttribute), false); if (tbattribute.Length > 0) { tablename = (tbattribute[0] as W.Data.TableAttribute).TableName;//获取特性的表名 } else { //如果没有用特性标记为表名称,默认用实体的名称作为表的名称 tablename = type.Name; } sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", tablename, fieldbuilder.ToString(), valuebuilder.ToString()); return GetActionParameter<T>(t, TableActionType.Insert); } /// <summary> /// 私有方法,获取利用实体更新表记录时的SQL语句和参数列表 /// </summary> /// <typeparam name="T">要更新的表对应的实体类型</typeparam> /// <param name="t">Model实体</param> /// <param name="sql">需要保存生成后的SQL语句</param> /// <returns>参数列表</returns> private static IList<IDataParameter> GetUpdateModelParameterT_SQL<T>(T t, out string sql) where T : new() { string tablename = string.Empty; StringBuilder pkbuilder = new StringBuilder(); StringBuilder fieldbuilder = new StringBuilder(); Type type = typeof(T); object[] attibute = type.GetCustomAttributes(typeof(W.Data.TableAttribute), false); if (attibute.Length > 0) { tablename = (attibute[0] as W.Data.TableAttribute).TableName; } else { tablename = type.Name; } foreach (PropertyInfo info in type.GetProperties()) { if (info.GetValue(t, null) != null) { object[] pkattibute = info.GetCustomAttributes(typeof(W.Data.ColumnAttribute), false); if (pkattibute.Length > 0 && (pkattibute[0] as W.Data.ColumnAttribute).IsPrimary) { //主键特性标记该属性为表的主键 pkbuilder.AppendFormat("AND {0}=@{0}", info.Name);//主键列于占位符 } else { fieldbuilder.AppendFormat("{0}=@{0},", info.Name);//设置列的赋值 } } } if (pkbuilder.Length <= 0) throw new Exception("来自X.CommandLib.dll.ORMHelper错误:没有主键的实体无法进行更新操作"); sql = string.Format("UPDATE {0} SET {1} WHERE 1=1{2}", tablename, fieldbuilder.ToString().Substring(0, fieldbuilder.ToString().Length - 1), pkbuilder.ToString()); return GetActionParameter<T>(t, TableActionType.Update); } /// <summary> /// 私有方法,用来提供以用Model实体来增加、删除、修改表记录时的参数列表 /// </summary> /// <typeparam name="T">要执行的实体类型</typeparam> /// <param name="t">Model实体</param> /// <returns> 参数列表</returns> private static IList<IDataParameter> GetActionParameter<T>(T t, TableActionType action) where T : new() { Type type = typeof(T); List<IDataParameter> parlist = new List<IDataParameter>(); foreach (PropertyInfo info in type.GetProperties()) { if (action == TableActionType.Insert) { object[] columbute = info.GetCustomAttributes(typeof(W.Data.ColumnAttribute), false); if (columbute.Length > 0) { if ((columbute[0] as W.Data.ColumnAttribute).IsIdentity) { continue;//如果是自增长就不处理这个属性 } } } object param = info.GetValue(t, null); switch (IDataSourceTypeFactory.DataSourceType) { case DataSourceType.SqlServer: parlist.Add(IDataParameterFactory.CreateParameterSingle(info.Name, param)); break; case DataSourceType.Oracl: parlist.Add(IDataParameterFactory.CreateParameterSingle(info.Name, param)); break; case DataSourceType.Access: parlist.Add(IDataParameterFactory.CreateParameterSingle(info.Name, param)); break; } } return parlist; } /// <summary> /// 私有方法,设置泛型实体类的主键字典 /// </summary> /// <typeparam name="T">泛型实体类型</typeparam> /// <param name="t">实体</param> private static void SetPKCache<T>(T t) where T : new() { List<string> pklist = new List<string>(); PropertyInfo[] propcoll = t.GetType().GetProperties();//获取该类型的所有属性 foreach (var item in propcoll) { object[] objattribute = item.GetCustomAttributes(typeof(W.Data.ColumnAttribute), false); if (objattribute.Length > 0) if ((objattribute[0] as W.Data.ColumnAttribute).IsPrimary)//如果该属性是用主键特性标记为主键的则添加为该类型的主键 pklist.Add(item.Name); } if (PKCache.ContainsKey(t.GetType().FullName)) PKCache[t.GetType().FullName] = pklist;//设置新的主键集合 else PKCache.Add(t.GetType().FullName, pklist); } /// <summary> /// 私有方法,获取泛型实体类的主键集合 /// </summary> /// <typeparam name="T">泛型实体类型</typeparam> /// <param name="t">实体</param> /// <returns>字典集合</returns> private static List<string> GetPKCache<T>(T t) where T : new() { if (PKCache.Count <= 0) SetPKCache<T>(t); if (PKCache.ContainsKey(t.GetType().FullName)) return PKCache[t.GetType().FullName];//直接返回 SetPKCache<T>(t); if (PKCache.ContainsKey(t.GetType().FullName)) return PKCache[t.GetType().FullName];//再次返回,如没有则说明该Model没有定义主键特性 return null; } #endregion } }
由于代码编写时的目的是供学习使用,所以在代码上可能存在不足,只要能说明问题就行了;