一.什么是ORM

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。

简单来说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中或者将数据库的数据拉取出来

二.EF基本原理

1.EF 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案

2.EF 核心对象DbContext,其基本原理是,实现系统IQueryable<T>接口,通过反射,获取SQL语句,操作数据库

三.模拟EF

1.模拟EF 首先要自定义解析lamdba表达式(解析表达式 是个难点,需要仔细调试)

1)构造表达式解析入口

 /// <summary>
/// 通过Lambda解析为Sql
/// </summary>
/// <param name="func"></param>
/// <returns></returns>
public static string GetSqlByExpression(Expression func, DirectionType dirType = DirectionType.None)
{
var getExp = func;
var result = "";
if (getExp is UnaryExpression)
{
result += VisitUnaryExpression((UnaryExpression)getExp);
}
if (getExp is BinaryExpression)
{
result += VisitBinaryExpression((BinaryExpression)getExp);
}
if (getExp is TypeBinaryExpression)
{
result += VisitTypeBinaryExpression((TypeBinaryExpression)getExp);
}
if (getExp is ConditionalExpression)
{
result += VisitConditionalExpression((ConditionalExpression)getExp);
}
if (getExp is ConstantExpression)
{
result += VisitConstantExpression((ConstantExpression)getExp);
}
if (getExp is ParameterExpression)
{
result += VisitParameterExpression((ParameterExpression)getExp);
}
if (getExp is MemberExpression)
{
result += VisitMemberExpression((MemberExpression)getExp, dirType);
}
if (getExp is LambdaExpression)
{
result += VisitLambdaExpression((LambdaExpression)getExp);
}
if (getExp is NewExpression)
{
result += VisitNewExpression((NewExpression)getExp);
}
if (getExp is NewArrayExpression)
{
result += VisitNewArrayExpression((NewArrayExpression)getExp);
}
if (getExp is InvocationExpression)
{
result += VisitInvocationExpression((InvocationExpression)getExp);
}
if (getExp is MemberInitExpression)
{
result += VisitMemberInitExpression((MemberInitExpression)getExp);
}
if (getExp is ListInitExpression)
{
result += VisitListInitExpression((ListInitExpression)getExp);
}
if (getExp is MethodCallExpression)
{
result += VisitMethodCallExpression((MethodCallExpression)getExp);
}
return result; }

lamdba解析入口

2)根据不同的类型,构建不同的解析方法

 /// <summary>
/// 判断包含变量的表达式
/// </summary>
/// <param name="func"></param>
/// <returns></returns>
private static string VisitMemberExpression(MemberExpression func, DirectionType dirType)
{
object value;
if (dirType == DirectionType.Left || dirType == DirectionType.None)
{
value = func.Member.Name;
}
else
{ switch (func.Type.Name)
{
case "Int32":
{
var getter = Expression.Lambda<Func<int>>(func).Compile();
value = getter();
}
break;
case "String":
{
var getter = Expression.Lambda<Func<string>>(func).Compile();
value = "'" + getter() + "'";
}
break;
case "DateTime":
{
var getter = Expression.Lambda<Func<DateTime>>(func).Compile();
value = "'" + getter().ToString("yyyy-MM-dd HH:mm:ss") + "'";
}
break;
default:
{
var getter = Expression.Lambda<Func<object>>(func).Compile();
value = getter();
}
break;
}
}
return value.ToString();
}
   private static string VisitUnaryExpression(UnaryExpression func)
{
var result = "";
result = GetSqlByExpression(func.Operand);
return result;
}
 private static string VisitBinaryExpression(BinaryExpression func)
{
//{(((p.Id == "1") AndAlso (p.OrderNo == "fasdf")) AndAlso (p.CreateTime == DateTime.Now))}
var result = "(";
result += "" + GetSqlByExpression(func.Left, DirectionType.Left) + "";
result += GetNodeType(func.NodeType);
result += "" + GetSqlByExpression(func.Right, DirectionType.Right) + "";
result += ")";
return result;
}
private static string VisitTypeBinaryExpression(TypeBinaryExpression func)
{
return "";
}
private static string VisitConditionalExpression(ConditionalExpression func)
{
return "";
}
 private static string VisitConstantExpression(ConstantExpression func)
{
var result = "";
if (func.Value.GetType() == typeof(String))
{
result += "'" + (func.Value.ToString()) + "'";
}
else if (func.Value.GetType() == typeof(Int32))
{
result += "" + (func.Value.ToString()) + "";
}
else
{
throw new Exception("请实现类型");
}
return result;
}
 private static string VisitParameterExpression(ParameterExpression func)
{
var propers = func.Type.GetProperties();
string result = ""; for (int i = ; i < propers.Length; i++)
{
var item = propers[i];
var itemStr = GetProperInfo(item);
if (!string.IsNullOrEmpty(itemStr))
{
result += itemStr + ",";
}
}
result = result.TrimEnd(',');
return result;
}
  /// <summary>
/// 判断包含函数的表达式
/// </summary>
/// <param name="func"></param>
/// <returns></returns>
private static String VisitMethodCallExpression(MethodCallExpression func)
{
var result = "";
if (func.Method.Name == "Where")
{
result += " Where ";
var cente = func.Arguments[];
result += GetSqlByExpression(cente);
}
else if (func.Method.Name.Contains("Contains"))
{
//获得调用者的内容元素
var getter = Expression.Lambda<Func<object>>(func.Object).Compile();
var data = getter() as IEnumerable;
//获得字段
var caller = func.Arguments[];
while (caller.NodeType == ExpressionType.Call)
{
caller = (caller as MethodCallExpression).Object;
}
var field = VisitMemberExpression(caller as MemberExpression, DirectionType.Left);
var list = (from object i in data select "'" + i + "'").ToList();
result += field + " IN (" + string.Join(",", list.Cast<string>().ToArray()) + ") ";
}
else if (func.Method.Name.Contains("Select"))
{
result += " Select ";
var cente = func.Arguments[];
result += GetSqlByExpression(cente);
}
return result;
}
  private static string VisitLambdaExpression(LambdaExpression func)
{
var result = "";
result += GetSqlByExpression(func.Body);
return result;
}
  private static string VisitNewExpression(NewExpression func)
{
var result = "";
result += GetSqlByExpression(func.Arguments[]);
return result;
}

3)根据 ExpressionType 判断条件类型

 private static string GetNodeType(ExpressionType expType)
{
var result = "";
if (expType == ExpressionType.AndAlso)
{
result += " and ";
}
if (expType == ExpressionType.Or)
{
result += " or ";
}
if (expType == ExpressionType.Equal)
{
result += " = ";
}
if (expType == ExpressionType.NotEqual)
{
result += " <> ";
}
if (expType == ExpressionType.Conditional)
{
result += " > ";
}
if (expType == ExpressionType.LessThan)
{
result += " < ";
}
if (expType == ExpressionType.GreaterThanOrEqual)
{
result += " >= ";
}
if (expType == ExpressionType.LeftShiftAssign)
{
result += " <= ";
}
return result;
}

4)根据ExpressionType 判断Expression 子类类型(本列中未用到)

         public static Expression GetExpression(Expression exp)
{
if (exp == null)
return exp;
switch (exp.NodeType)
{
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return (UnaryExpression)exp;
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return (BinaryExpression)exp;
case ExpressionType.TypeIs:
return (TypeBinaryExpression)exp;
case ExpressionType.Conditional:
return (ConditionalExpression)exp;
case ExpressionType.Constant:
return (ConstantExpression)exp;
case ExpressionType.Parameter:
return (ParameterExpression)exp;
case ExpressionType.MemberAccess:
return (MemberExpression)exp;
case ExpressionType.Call:
return (MethodCallExpression)exp;
case ExpressionType.Lambda:
return (LambdaExpression)exp;
case ExpressionType.New:
return (NewExpression)exp;
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return (NewArrayExpression)exp;
case ExpressionType.Invoke:
return (InvocationExpression)exp;
case ExpressionType.MemberInit:
return (MemberInitExpression)exp;
case ExpressionType.ListInit:
return (ListInitExpression)exp;
default:
throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
}

2.构建上下文类型IECContext

  public class IECContext: IDisposable
{
public static string ConnectionString { get; set; }
public static string ConnectionKey { get; set; } public IECContext(string key)
{
ConnectionKey = key;
ConnectionString = ConfigurationManager.ConnectionStrings[key].ConnectionString;
} public void Dispose()
{
this.Dispose();
}
}

3.构建DBSet 对象,需要实现IQueryable<T> 接口

 public class DBSet<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable
{ QueryProvider provider;
Expression expression;
public DBSet(QueryProvider provider)
{ if (provider == null)
{
throw new ArgumentNullException("QueryProvider不能为空");
}
this.provider = provider;
this.expression = Expression.Constant(this);
} public DBSet(QueryProvider provider, Expression expression)
{
if (provider == null)
{ throw new ArgumentNullException("QueryProvider不能为空");
} if (expression == null)
{
throw new ArgumentNullException("Expression不能为空");
} if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
{
throw new ArgumentOutOfRangeException("类型错误");
} this.provider = provider;
this.expression = expression;
}
Expression IQueryable.Expression
{
get { return this.expression; }
} Type IQueryable.ElementType
{
get { return typeof(T); }
}
IQueryProvider IQueryable.Provider
{
get { return this.provider; }
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator();
} }

3.1添加 Incloud方法,兼容多表链接查询(比较重要)

 public DBSet<T> Incloud(string cloudName)
{
if (provider.CloudNames == null)
{
provider.CloudNames = new Queue<string>();
}
provider.CloudNames.Enqueue(cloudName);
return this;
}

4.实现 IQueryProvider接口

public abstract class QueryProvider : IQueryProvider
{
protected QueryProvider() { }
public string SQL { get; set; } public Queue<string> CloudNames { get; set; }
IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression)
{
var sqlWherr = ExpressionHelp.GetSqlByExpression(expression);
if (string.IsNullOrEmpty(SQL))
{
if (sqlWherr.ToLower().Contains("select"))
{
SQL = string.Format("{0} from {1}", sqlWherr, GetTableName<S>());
}
else
{ SQL = string.Format("select * from {0} {1}", GetTableName<S>(), sqlWherr);
}
}
else
{
if (sqlWherr.ToLower().Contains("select"))
{
SQL = string.Format("{0} from ({1}) t", sqlWherr, SQL);
}
else
{ SQL = string.Format("select * from ({0}) t {1}", sqlWherr, GetTableName<S>());
}
} return new DBSet<S>(this, expression);
}
private string GetTableName<T>()
{
return TableName();
}
IQueryable IQueryProvider.CreateQuery(Expression expression)
{
return null; }
S IQueryProvider.Execute<S>(Expression expression)
{
return (S)this.Execute(expression);
} object IQueryProvider.Execute(Expression expression)
{
return this.Execute(expression);
} public abstract object Execute(Expression expression);
public abstract string TableName();
}

5.最后 实现 ToQueryList 扩展方法,将转换成功的SQL 链接数据库查询(比较重要,也比较麻烦)

 public static List<T> ToQueryList<T>(this IQueryable<T> query)
{
var sql = query.ToString();
ExecProcSql execProc = new ExecProcSql(IECContext.ConnectionKey);
var dataSet = execProc.ExecuteDataSet(sql);
var dt = dataSet.Tables[];
var list = dt.DataSetToList<T>();
var myQuery = query as DBSet<T>; if (myQuery != null)
{
var queue = myQuery.GetColudNames();
if (queue.Count > )
{
var count = queue.Count;
for (int i = ; i < count; i++)
{
var coludName = queue.Dequeue(); list = GetClouds(default(T), list, coludName);
}
} } return list;
}

5.1 应用 反射 和递归,进行字表数据查询,复制(比较重要,也比较麻烦)

 private static List<T> GetClouds<T>(T t, List<T> list, string cloudName)
{
if (list == null)
{
list = new List<T>();
if (t != null)
{
list.Add(t);
}
} var result = list;
var clouds = cloudName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (clouds.Count <= )
{
return result;
}
var proper = typeof(T).GetProperty(clouds[]);
if (proper == null)
{
throw new Exception("属性不存在");
}
string sql = "";
List<string> ids = new List<string>();
for (int i = ; i < result.Count; i++)
{
var p = typeof(T).GetProperty("Id");
if (p == null)
{
throw new Exception("必须存在Id 列");
}
var id = p.GetValue(result[i]);
if (id != null)
{
ids.Add(id.ToString());
}
} clouds.RemoveAt();
//如果是 一对多 对象
if (proper.PropertyType.GetInterface("IEnumerable") == typeof(System.Collections.IEnumerable))
{
var pType = proper.PropertyType.GetGenericArguments()[];
sql = string.Format("SELECT * FROM {0} where {1} in ({2})", pType.Name, typeof(T).Name + "_Id", string.Join(",", ids.Select(p => "'" + p + "'").ToArray()));
ExecProcSql execProc = new ExecProcSql(IECContext.ConnectionKey);
var dataSet = execProc.ExecuteDataSet(sql);
var dt = dataSet.Tables[];
GetDataSetToList(dt, result, proper.Name, clouds);
}
else//如果是一对一 对象
{
sql = string.Format("select * from {0} where {1} in({2})", typeof(T).Name, "Id", string.Join(",", ids.Select(p => "'" + p + "'").ToArray())); ExecProcSql execProc = new ExecProcSql(IECContext.ConnectionKey);
var dataSet = execProc.ExecuteDataSet(sql);
///T 类型的集合
var dt1 = dataSet.Tables[];
ids = new List<string>();
//var preItem=
for (int i = ; i < dt1.Rows.Count; i++)
{
var dr = dt1.Rows[i];
if (dt1.Columns.Contains(proper.Name + "_Id"))
{
var value = dr[proper.Name + "_Id"].ToString();
ids.Add(value);
} }
ids = ids.Distinct().ToList();
if (ids.Count <= )
{
return result;
}
sql = string.Format("select * from {0} where {1} in({2})", proper.PropertyType.Name, "Id", string.Join(",", ids.Select(p => "'" + p + "'").ToArray()));
var dataSet2 = execProc.ExecuteDataSet(sql);
///cloudName 类型的集合
var dt2 = dataSet2.Tables[];
CloudDataTableToList(dt1, dt2, result, proper.Name, clouds);
}
return result; }

由此,自己的ORM 基本完成(这里只介绍查询,新增,修改,删除相对而言比较简单,不做具体介绍)

四.配置,测试

1.继承IECContext

 public class ECContext : IECContext
{
public ECContext() : base("DBContext")
{
QueryOrder = new DBSet<OrdersInfo>(new MyQueryProvider<OrdersInfo>());
}
public DBSet<OrdersInfo> QueryOrder { get; set; } public DBSet<User> User { get; set; } public DBSet<OrderItemInfo> OrderItemInfo { get; set; } public DBSet<ProductInfo> ProductInfo { get; set; } public DBSet<Info> Info { get; set; }
}

2.添加业务模型,以订单,订单项,产品,产品信息 为列

 public class OrdersInfo
{
public string Id { get; set; } public string OrderNo { get; set; } public DateTime CreateTime { get; set; } public int Member { get; set; } public MemberType MemberType { get; set; } public User User { get; set; } public List<OrderItemInfo> OrderItems { get; set; } } public class User
{
public string Id { get; set; } public string Name { get; set; }
} public class OrderItemInfo
{
public string Id { get; set; }
public OrdersInfo Order { get; set; } public string ProductName { get; set; } public ProductInfo ProductInfo { get; set; }
}
public class ProductInfo
{
public string Id { get; set; }
public string ProductName { get; set; } public Info Info { get; set; } }
public class Info
{
public string Id { get; set; } public decimal Price { get; set; }
}
public enum MemberType
{
None =
}

3.配送config

<connectionStrings>
<add name="DBContext" connectionString="server=.;database=test01;uid=sa;pwd=12345678" />
</connectionStrings>

4.添加测试数据

5.添加测试代码

 static void Main(string[] args)
{
using (ECContext context = new ECContext())
{
var listst = new List<string> {
"",
"",
"", };
//var obj = context.QueryOrder.Incloud("OrderItemInfo").Where(p => p.Id == "1" && p.OrderNo == "fasdf" && p.Member == 1 && p.CreateTime > DateTime.Now && listst.Contains(p.OrderNo));
var m = ;
var date = DateTime.Now;
var objInfo2 = context.QueryOrder.Incloud("OrderItems.ProductInfo.Info").Where(p => p.Id == "").ToQueryList(); //var obj = context.QueryOrder.Where(p => listst.Contains(p.OrderNo));
//var obj = context.QueryOrder.Where(p => p.Id == "1"&&p.OrderNo=="fasdf" &&p.Member==1 && p.CreateTime>DateTime.Now&& listst.Contains(p.OrderNo));
return;
}
}

6.运行

 五.写在结尾

本文重点是 如何解析lamdba表达式 和字表查询部分。由于是自己写的一个demo,实在 看不上眼,就不上传源码了

本文对于EF 里的 缓存机制,反射机制,数据缓存机制 不做介绍,在demo中 也没有涉及,只是简单的一个类似于EF的 orm框架

EF 用的时间比较长,一直想写一个自己的ORM 挑战下自己,所以花了一天半左右的时间 写了一个简单的实现,可能会有很多bug ,后期努力。

欢迎各位指正,谢谢

模拟EF CodeFist 实现自己的ORM的更多相关文章

  1. Winform EF CodeFist方式连接数据库

    直接生成ado.net 实体数据模型挺方便的,但只有一步步的手写代码才能更好的理解EF,在学习asp.net core过程中手写代码已经明白了怎么回事,但实现过程有些麻烦不知道如何记录,但Winfor ...

  2. 用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper原理分析与测试手记

    [本文篇幅较长,可以通过目录查看您感兴趣的内容,或者下载格式良好的PDF版本文件查看] 目录 一.ORM的"三国志"    2 1,PDF.NET诞生历程    2 2,Linq2 ...

  3. (转)用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper原理分析与测试手记

    原文地址:http://www.cnblogs.com/bluedoctor/p/3378683.html [本文篇幅较长,可以通过目录查看您感兴趣的内容,或者下载格式良好的PDF版本文件查看] 目录 ...

  4. 模仿EF,我们用JS开发的HTML5 SQLite 访问库

    今天终于有空把demo放到了RunJS上面去.请使用google chrome观看在线演示: http://sandbox.runjs.cn/show/pekbd9zb 这个库本来是我们开发的phon ...

  5. EF是啥?【What is Entity Framework?】(EF基础系列2)

    EF产生的背景: 编写ADO.NET访问数据的代码,是沉闷而枯燥的,所以微软提供了一个对象关系映射框架(我们称之为EF),通过EF可以自动帮助我们的程序自动生成相关数据库. Writing and m ...

  6. ORM系列之三:Dapper

    目录 1.Dapper 简介 2.Dapper 安装 3.Dapper 使用 Dapper简介 Dapper是一个轻量级的ORM框架,短小精悍,正如其名.对于小项目,使用EF,NHibernate这样 ...

  7. 你是否还在质疑EF的性能

    1. 写在前面的话 一直没有写博客的习惯,感觉太浪费时间,没有那么多精力,其实仔细一想,写博客是一种习惯,也是一种心境,同时也是对自己所掌握的知识结构的一个梳理过程,对自己知识体系的一个巩固,同时也是 ...

  8. .Net 自己写个简单的 半 ORM (练手)

    ORM 大家都知道, .Net 是EF  还有一些其他的ORM  从JAVA 中移植过来的 有 , 大神自己写的也有 不管ORM 提供什么附加的 乱七八糟的功能 但是 最主要的 还是 关系映射 的事情 ...

  9. Mego(2) - NET主流ORM框架分析

    接上文我们测试了各个ORM框架的性能,大家可以很直观的看到各个ORM框架与原生的ADO.NET在境删改查的性能差异.这里和大家分享下我对ORM框架的理解及一些使用经验. ORM框架工作原理 典型ORM ...

随机推荐

  1. Chapter 5:Spectral-Subtractive Algorithms

    作者:桂. 时间:2017-05-24  10:06:39 主要是<Speech enhancement: theory and practice>的读书笔记,全部内容可以点击这里. 书中 ...

  2. 【原创】源码角度分析Android的消息机制系列(三)——ThreadLocal的工作原理

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Android源码(API24)中对ThreadLocal的定义: public class ThreadLocal<T> 即 ...

  3. 循环执行sql语句

    DECLARE --声明变量 SQL_ALLTABLES LONG; SQL_INSERT LONG; TYPE THE_CURSOR_TYPE IS REF CURSOR; --定义引用游标的数据类 ...

  4. jmeter 实现DB数据与接口数据的匹配校验

    前言:接口出参数据与DB数据结合校验,使校验力度更准确~ jmeter自带插件JDBC Request Sampler 这个Sampler可以向数据库发送一个jdbc请求(sql语句),并获取返回的数 ...

  5. Linux命令 查看文件内容

    cat [功能说明] 查看文件的内容  #cat本身是一个串接命令,把指定一个或多个源文件的内容,利用>符号重定向到目标文件中,如果不指定重定向文件,则默认在标准输出设备上显示.此时,可以利用c ...

  6. 10分钟轻松学会python turtle绘图

     1. 画布(canvas) 1.1 相关函数: 2. 画笔 2.1 画笔的状态 2.2 画笔的属性 2.3 绘图命令 3. 命令详解 4. 绘图举例 4.1 太阳花 4.2 绘制小蟒蛇 4.3 绘 ...

  7. SICP-1.7-递归函数

    递归函数 函数内部直接或间接的调用函数自身 将复杂问题简单化 例子程序 def sum_digits(n): """Return the sum of the digit ...

  8. dubbo与zookeeper的关系

    Dubbo建议使用Zookeeper作为服务的注册中心. 1.   Zookeeper的作用: zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是 ...

  9. unity3d项目导入android studio

    第一步 拿到unity3d项目,里面包含以下文件 第二步 在android studio下新建一个project 第三步 将unity3d项目目录下的libs下的jar文件复制黏贴到android s ...

  10. php打包文件为ZIP包后下载到本地

    这是一个工作中需要打包下载当前产品的所有图片到本地,文件格式为ZIP压缩包,打包下载文件跟图片一样,本程序细节为实际情况,使用需按照自己实际情况书写:<?php/**************** ...