博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现简单的ORM
阅读量:5010 次
发布时间:2019-06-12

本文共 6667 字,大约阅读时间需要 22 分钟。

介绍

本篇将介绍实现简单的ORM,即:对数据表的通用操作:增、删、改、查

数据访问层

数据访问层类图

 

类说明:

1.DbProvider(供应):为数据操作提供基本对象,如:连接、操作对象、事务。。。

2.DbContext(环境):执行数据操作,如:返回DataReader、执行单条语句、执行事务。。。

3.SqlContext(SQLServer环境):针对不同数据库的操作环境。(本例为针对SQLServer)

代码说明:

public int ExecuteNonQuery(Func
tranExecuteSQL){ using (DbConnection conn = m_Provider.CreateConnection(m_ConnString)) { DbTransaction tran = m_Provider.CreateTransaction(conn); using (DbCommand cmd = m_Provider.CreateCommand(conn)) { int num; m_Provider.PrepareCommand(conn, cmd, tran); try { num = tranExecuteSQL(cmd); tran.Commit(); } catch { tran.Rollback(); throw; } return num; } }}
View Code

这里主要说明事务实现的代码(其余代码可以在文章末尾下载源码):要想保证事务的特性,就必须保证事务中的语句用的是同一个DbCommand对象

要想保证事务中的所有语句使用的是同一个DbCommand对象,最简单的方法就是:DbCommand对象在事务方法中创建,执行语句由外部传入

所以:该方法的签名为一个Func<DbCommand,int>的委托,调用者可以直接获取到DbCommand对象执行语句,而真正的DbCommand对象统一由事务方法创建

数据表字段映射层

表映射代码:

/// /// 表特性/// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)]public class TableAttribute : Attribute{    ///     /// 表名    ///     public string TableName { get; set; }    ///     /// 前缀(列)    ///     public string Prefix { get; set; }    public TableAttribute(string name = "", string prefix = "")    {        if(!string.IsNullOrWhiteSpace(name))            this.TableName = name;        if(!string.IsNullOrWhiteSpace(prefix))            this.Prefix = prefix;    }}
View Code

列映射代码:

/// /// 行特性/// [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]public class ColumnAttribute : Attribute{    ///     /// 列明    ///     public string ColumnName { get; set; }    ///     /// 是否是主键    ///     public bool IsPrimaryKey { get; set; }    ///     /// 是否是自增列    ///     public bool IsIdentity { get; set; }    public ColumnAttribute() { }    public ColumnAttribute(string columnName)    {        ColumnName = columnName;    }}
View Code

代码很简单,且有注释,这里就不复述了

实体映射层

实体映射应该算ORM框架的核心,分解实体层要做的事,然后逐步讲解代码实现

1.实体属性-表字段的映射关系

代码:

/// /// 得到类型所有实例属性(含特性)/// /// 
类型
///
键值对:Key:表特性;值:属性名-列特性
public static KeyValuePair
> GetPropertiesForModel
() where T : new(){ KeyValuePair
> KVProperties; Type type = typeof(T); //表 TableAttribute tableAttr = type.GetCustomAttribute
(); if (tableAttr == null) tableAttr = new TableAttribute(name: type.Name); KVProperties = new KeyValuePair
>(tableAttr, new Dictionary
{ }); //属性 PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo proInfo in properties) { //类直接过滤 if (!proInfo.GetType().IsClass) continue; ColumnAttribute attr = proInfo.GetCustomAttribute
(); KVProperties.Value.Add(proInfo.Name, attr ?? new ColumnAttribute(proInfo.Name)); } return KVProperties;}
View Code

代码很简单,这里就不再复述了

但有一点要说明:当属性上没有使用列特性时,表明列名与属性名称一致,所以列特性中存放其列名称(即:属性名称)。这样做的目的是方便后面组织sql语句

2.根据不同的数据库操作命令(INSERT、DELETE、UPDATE、SELECT),组织生成对应的SQL语句。这里重点介绍一下组织SQL参数的方法

代码:

/// /// 生成SQL语句/// /// 
类型
/// 实体/// 操作类型/// 条件语句///
Item1:操作sql语句;Item2:参数数组;Item3:列名-属性名
internal static Tuple
> OperateSQL(T model, Operate operate, string operateSql){ var modelInfo = GetCache(model); Dictionary
pmc = GetPropertyMappColumn(modelInfo.Key.Prefix, modelInfo.Value); StringBuilder sql = new StringBuilder(100); LinkedList
parms = new LinkedList
(); //操作表语句(表名不可以通过@参数形式传递,所以只能采用拼接) sql.AppendFormat(operateSql, modelInfo.Key.TableName); if (model != null) { switch (operate) { case Operate.SELECT: SelectSQL(model, pmc, sql, parms); break; case Operate.INSERT: InsertSQL(model, pmc, sql, parms); break; case Operate.UPDATE: UpdateSQL(model, pmc, sql, parms); break; case Operate.DELETE: DeleteSQL(model, pmc, sql, parms); break; } } return new Tuple
>(sql.ToString(), parms.ToArray(), pmc.ToDictionary(c => c.Key, c => c.Value == null ? c.Key : c.Value.ColumnName));}
View Code

介绍:

a.根据不同的数据库操作命令,调用生成不同的SQL语句的方法

b.返回:生成的SQL语句,数据库参数数组,实体属性名-数据表字段名的对应关系

该方法只是提供给外部调用的方法,真正处理SQL参数语句的方法是下面这个方法

代码:

/// /// 准备参数语句/// /// 
类型
/// 实体类/// 属性-列特性/// 条件格式/// sql参数///
Item1:有值得属性名称;Item2:条件
private static Tuple
, LinkedList
> ProvieParameter(T model, Dictionary
pmc, Func
parmFormat, LinkedList
parms){ //属性集合 LinkedList
propertyNames = new LinkedList
(); //条件集合 LinkedList
conditions = new LinkedList
(); foreach (string key in pmc.Keys) { //属性值 object propertyValue = model.GetType().GetProperty(key).GetValue(model); if (propertyValue == null) continue; propertyNames.AddLast(key); //条件 if (condFormat != null) conditions.AddLast(condFormat(pmc[key].ColumnName, key)); //参数 parms.AddLast(SqlContext.GetInstance().CreateParameter(string.Format("@{0}", key), propertyValue)); } return new Tuple
, LinkedList
>(propertyNames, conditions);}
View Code

介绍:

a.处理三个集合:属性集合、条件集合、参数集合

b.根据传入的属性名-列特性(pmc)参数:将属性提取出来保存入属性集合

c.根据传入的SQL条件格式(Func<列名称,参数名称,组合生成条件SQL语句> condFormat)参数:生成条件集合。如:@参数名,@参数名...(INSERT语句);字段名=@参数名,字段名=@参数名...(SELECT与DELETE语句)

d.根据传入的实体(属性与属性值):填充SQL参数集合

实体框架上下文

代码:

public static int Insert
(T model) where T : new(){ if (model == null) throw new ArgumentNullException(model.ToString()); //sql语句、sql参数 Tuple
> sqlParameter = EFToSQL
.OperateSQL(model, Operate.INSERT, "INSERT INTO {0}"); return SqlContext.GetInstance().ExecuteNonQuery(sqlParameter.Item1, sqlParameter.Item2);}
View Code

这里只贴出了INSET方法,其余方法可下载源码查看

测试调用

代码:

static void Main(string[] args){    IList
users = EFContext.Select
(null); if (users != null) { foreach (User user in users) { Console.WriteLine("用户名:{0},GUID:{1}", user.UserName, user.RecordId); } }}
View Code

本篇就介绍到这里,感谢大家的耐心阅读。

下一篇将介绍:反射的优化

转载于:https://www.cnblogs.com/color-wolf/p/6784800.html

你可能感兴趣的文章
regsvr32注册COM组件失败
查看>>
jmeter,CSV数据加载、数据库连接、正则
查看>>
(独孤九剑)--正则表达式
查看>>
MySQL学习点滴 --分区表
查看>>
4.6.1 测试基础
查看>>
洛谷 P2486 [SDOI2011]染色
查看>>
oo第三单元总结
查看>>
leetcode : Count and Say [基本功]
查看>>
洛谷 P2485 [SDOI2011]计算器 解题报告
查看>>
c#访问存储过程
查看>>
Slickflow.NET 开源工作流引擎基础介绍(三) -- 基于HTML5/Bootstrap的Web流程设计器
查看>>
Node教程
查看>>
java将字段映射成另一个字段,关于 接口传参 字段不对应转换
查看>>
Redis
查看>>
字段和属性的区别
查看>>
HTTP(一)工作机制
查看>>
条形码扫描枪数据读取的问题
查看>>
$this->autoRender = false
查看>>
健壮的 Java 基准测试
查看>>
phpstorm查看类的继承关系
查看>>