一.什么是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. 关于angular-route后获取路由标签的一些问题

    要实现angular路由,我们需要用到angular.js和angular-route.js 在接入网络的情况下,很多网站都可以下载到这个文件. 然后呢,将文件引入到你的HTML中,然后是基础格式 h ...

  2. Linux常见命令(二)

    随着Linux应用的扩展许多同学开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起.虽然Linux桌面应用发展很快,但是命令在Linux中依然有很强的生命力.Li ...

  3. CSS 公共样式

    global.css | reset.css(格式化样式) common.css(公共组件样式) layout.css(当前页面样式) 清除全站所有页面的浏览器默认样式,保证在初始样式在所有浏览器下一 ...

  4. Chapter 3:Speech Production and Perception

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

  5. 微信小程序代开发

    微信申请第三方之后可以获取授权方的很多权限,主要的是生码和待开发,生码的第三方授权之前已经写了一篇文章,最近做了小程序待开发,总结一下写下来供大家参考 注意事项:如果在调试过程中返回了错误码请到小程序 ...

  6. 【转载】Sublime Text 3065 Keygen and Patcher

    原始日期:2014-10-01 18:25      差不多时隔一年了,Sublime Text 终于更新啦!相信很多友友都已经升级到3065版本了,所以我也特地抽空为大家做了个新版补丁.该补丁仅作为 ...

  7. jQuery Ajax封装(附带加载提示和请求结果提示模版)

    1.创建HTML文件(demo) <!doctype html> <html lang="en"> <head> <meta charse ...

  8. Normalize.css源码注释翻译&浏览器css兼容问题的理解

    版本v5.0.0源码地址: https://necolas.github.io/normalize.css/5.0.0/normalize.css 翻译版: /*! normalize.css v5. ...

  9. ecshop的aes加密(封装)

    从一家做shopex,ecstore的公司到一家做b2b的ecshop的公司...来了就要实战,其他的不说了,先来了解什么是php的aes加密吧? aes(高级加密标准),AES的区块长度固定为128 ...

  10. 解决运行pytorch程序多线程问题

    当我使用pycharm运行  (https://github.com/Joyce94/cnn-text-classification-pytorch )  pytorch程序的时候,在Linux服务器 ...