介绍

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

数据访问层

数据访问层类图

类说明:

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

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

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

代码说明:

public int ExecuteNonQuery(Func<DbCommand, int> 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;
}
}
}

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

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

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

数据表字段映射层

表映射代码:

/// <summary>
/// 表特性
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)]
public class TableAttribute : Attribute
{
/// <summary>
/// 表名
/// </summary>
public string TableName { get; set; } /// <summary>
/// 前缀(列)
/// </summary>
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;
}
}

列映射代码:

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

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

实体映射层

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

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

代码:

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

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

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

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

代码:

/// <summary>
/// 生成SQL语句
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="model">实体</param>
/// <param name="operate">操作类型</param>
/// <param name="operateSql">条件语句</param>
/// <returns>Item1:操作sql语句;Item2:参数数组;Item3:列名-属性名</returns>
internal static Tuple<string, DbParameter[], Dictionary<string, string>> OperateSQL(T model, Operate operate, string operateSql)
{
var modelInfo = GetCache(model);
Dictionary<string, ColumnAttribute> pmc = GetPropertyMappColumn(modelInfo.Key.Prefix, modelInfo.Value); StringBuilder sql = new StringBuilder();
LinkedList<DbParameter> parms = new LinkedList<DbParameter>(); //操作表语句(表名不可以通过@参数形式传递,所以只能采用拼接)
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<string, DbParameter[], Dictionary<string, string>>(sql.ToString(), parms.ToArray(),
pmc.ToDictionary(c => c.Key, c => c.Value == null ? c.Key : c.Value.ColumnName));
}

介绍:

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

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

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

代码:

/// <summary>
/// 准备参数语句
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="model">实体类</param>
/// <param name="pmc">属性-列特性</param>
/// <param name="condFormat">条件格式</param>
/// <param name="parms">sql参数</param>
/// <returns>Item1:有值得属性名称;Item2:条件</returns>
private static Tuple<LinkedList<string>, LinkedList<string>> ProvieParameter(T model, Dictionary<string, ColumnAttribute> pmc, Func<string, string, string> parmFormat, LinkedList<DbParameter> parms)
{
//属性集合
LinkedList<string> propertyNames = new LinkedList<string>();
//条件集合
LinkedList<string> conditions = new LinkedList<string>(); 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<string>, LinkedList<string>>(propertyNames, conditions);
}

介绍:

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

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

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

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

实体框架上下文

代码:

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

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

测试调用

代码:

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

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

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

源码下载

实现简单的ORM的更多相关文章

  1. 一个简单的ORM制作(SQL帮助类)

    一个简单的ORM制作大概需要以下几个类: SQL执行类 CURD操作类 其他酱油类 先从SQL执行类说起,可能会涉及数据库的迁移等问题,所以需要定义一个接口以方便迁移到其他数据库, 事务没提供命名,若 ...

  2. D2010 RTTI + Attribute 简单实现ORM

    还记得David I 今年四月来盛大时,被问及“反射机制能再做得好一点吗?我们想放弃RTTI”,David I 回答“这的确是需要考虑的地方,当然RTTI我们不会放弃的”.(这个白胡子的老哥哥还真很可 ...

  3. C#基础笔记---浅谈XML读取以及简单的ORM实现

    背景: 在开发ASP.NETMVC4 项目中,虽然web.config配置满足了大部分需求,不过对于某些特定业务,我们有时候需要添加新的配置文件来记录配置信息,那么XML文件配置无疑是我们选择的一个方 ...

  4. C#基础---浅谈XML读取以及简单的ORM实现

    背景: 在开发ASP.NETMVC4 项目中,虽然web.config配置满足了大部分需求,不过对于某些特定业务,我们有时候需要添加新的配置文件来记录配置信息,那么XML文件配置无疑是我们选择的一个方 ...

  5. metadata的使用以及简单的orm模式

    使用sqllite3和metadata简单的封装了个简单的orm #!/usr/bim/python #-*-coding: utf-8 -*- import threading import sql ...

  6. Delphi2010 RTTI + Attribute 简单实现ORM实例

    1.支持ORM,最基础的两个信息是表的信息和字段信息.这两个信息,如果用Attribute 来辅助,代码更简洁和可读性更好.可以把属性名当做真实字段名,也可以将特性里的属性当成真实姓名,再加上字段标题 ...

  7. 通过反射来手写简单的ORM SQlserver

    不说废话,直接上干货,如发现问题,欢迎大家指出,谢谢! //------------------------------------MySQlServerORM [简单 CURD] using Sys ...

  8. 简单理解ORM,实体类生成查询SQL语句

    目前有很多开源的ORM项目,大多情况下也不需要我们重复去造轮子,我们只需要了解轮子怎么造的,怎么用就可以,下面简单说一下怎么通过实体生成一个SQL语句: 先建立2个Attribute类,TableAt ...

  9. python-元类和使用元类实现简单的ORM

    元类 面向对象中,对象是类的实例,即对象是通过类创建出来的,在python中,一切皆对象,同样,类也是一个对象,叫做类对象,只是这个类对象拥有创建其子对象(实例对象)的能力.既然类是对象,那么类是通过 ...

随机推荐

  1. Pycharm模板添加默认信息

    我们在Pycharm中使用到python3.x版本的解释器,完全没有问题可以正常使用,但是有的时候多少会出现使用python2的时候 我们明明都把代码和文字注释了,为什么使用的时候还会报错呢?? 报错 ...

  2. PAT 1040 Longest Symmetric String[dp][难]

    1040 Longest Symmetric String (25)(25 分) Given a string, you are supposed to output the length of th ...

  3. Oracle多关键字模糊查询

    以前写SQL时,知道MySQL多字段模糊查询可以使用[charlist] 通配符,如: SELECT * FROM Persons WHERE City LIKE '[ALN]%'但是在Oracle中 ...

  4. 机器学习理论基础学习15---条件随机场(CRF)

    一.CRF的由来HMM->MEMM->CRF 二.HMM到MEMM MEMM打破了HMM的观测条件独立假设 三.MEMM到CRF CRF克服了MEMM的label bias problem ...

  5. ps中的栅格化--引出--矢量图

    矢量图使用直线和曲线来描述图形,这些图形的元素是一些点.线.矩形.多边形.圆和弧线等等,它们都是通过数学公式计算获得的.例如一幅花的矢量图形实际上是由线段形成外框轮廓,由外框的颜色以及外框所封闭的颜色 ...

  6. Summary: rand5构造rand7

    给一个方法,比如 rand5(), 它能够等概率生成 1-5 之间的整数. 所谓等概率就是1,2,3,4,5 生产的概率均为 0.2 .现在利用rand5(), 构造一个能够等概率生成 1- 7 的方 ...

  7. cocos代码研究(2)Layer学习笔记

    auto layer = Layer::create(); /*************华丽分割线*************/ auto layer = LayerColor::create(Colo ...

  8. 网络编程—代码—UDP数据报传输

    UDP:数据报传输 1.接收端 public class Udps { //接收端 public static void main(String[] args) throws IOException ...

  9. POI导出EXCEL经典实现(转)

    http://www.cnblogs.com/xwdreamer/archive/2011/07/20/2296975.html 1.Apache POI简介 Apache POI是Apache软件基 ...

  10. linux 图形界面的安装

    本文内容主要摘自:http://blog.csdn.net/hotday_kevin/article/details/8310054 文中图形的安装和卸载都给了两种方式,貌似是因为都是版本的问题而不同 ...