上篇文章标题乱起,被吐槽了,这次学乖了。

上篇文章中介绍了如何解析Expression生成对应的SQL语句,以及IQueryable的一些概念,以及我们所搭建的框架的思想等。但还没把它们结合并应用起来。这一篇文章将更黄更暴力,揭露IQueryable在实际使用中延迟加载的实现原理,结合上篇对Expression的解析,我们来实现一个自己的“延迟加载”

如果还不太了解如何解析Expression和IQueryable的一些基本概念,可以先看看我的上篇文章

我们先来做些基本工作,定义一个IDataBase接口,里面可以定义些查询,删除,修改,新增等方法,为了节约时间,我们就定义一个查询和删除的方法,再定义一个获取IQueryable<T>实例的方法

   public interface IDataBase
{
List<T> FindAs<T>(Expression<Func<T, bool>> lambdawhere);
int Remove<T>(Expression<Func<T, bool>> lambdawhere);
IQueryable<T> Source<T>();
}

再添加一个类DBSql,实现我们上面的IDataBase接口,这个类是负责提供对sql数据库的操作

 public class DBSql : IDataBase
{
public List<T> FindAs<T>(Expression<Func<T, bool>> lambdawhere)
{
throw new NotImplementedException();
} public int Remove<T>(Expression<Func<T, bool>> lambdawhere)
{
throw new NotImplementedException();
} public IQueryable<T> Source<T>()
{
throw new NotImplementedException();
}
}

IQueryable<T>

上篇文章中有个朋友的回复对IQueryable的解释十分到位,“IQueryable只存贮条件,不立即运行,从而可以实现延迟加载。”那它是如何存贮条件,如何延迟加载的?

这时我们为了提供 public IQueryable<T> Source<T>() 所需的对象。我们再来建一个SqlQuery类,实现IQueryable<T>。

   public class SqlQuery<T> : IQueryable<T>
{
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
} System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
} public Type ElementType
{
get { throw new NotImplementedException(); }
} public Expression Expression
{
get { throw new NotImplementedException(); }
} public IQueryProvider Provider
{
get { throw new NotImplementedException(); }
}
}

看到这里大家都不陌生吧?

GetEnumerator()是IEnumerable<T>里的。有了它我们就能foreach了。有泛型和非泛型版本,所以有2个

Type提供访问当前对象的类型(反正由你定义。。。)

Expression是贮存查询条件的

IQueryProvider简单的翻译过来就是查询提供者,它是负责创建查询条件和执行查询的。我们写一个SqlProvider类来实现它

  public class SqlProvider<T> : IQueryProvider
{ public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
throw new NotImplementedException();
} public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
} public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
} public object Execute(Expression expression)
{
throw new NotImplementedException();
}
}

CreateQuery是创建查询条件。。

平时我们

IQueryable query=xxx源;

query=query.Where(x=>x.Name=="123");

这时Where方法里做的其实就是将前面query的Expression属性和Where里的(x=>x.Name=="123")相并,并且调用Provider属性里的CreateQuery方法。我们可以把我们的代码改成这样,来看看到底是不是这么回事。

   public class DBSql : IDataBase
{
public IQueryable<T> Source<T>()
{
return new SqlQuery<T>();
} public List<T> FindAs<T>(Expression<Func<T, bool>> lambdawhere)
{
throw new NotImplementedException();
} public int Remove<T>(Expression<Func<T, bool>> lambdawhere)
{
throw new NotImplementedException();
}
} public class SqlQuery<T> : IQueryable<T>
{ private Expression _expression;
private IQueryProvider _provider; public SqlQuery()
{
_provider = new SqlProvider<T>();
_expression = Expression.Constant(this);
} public SqlQuery(Expression expression, IQueryProvider provider)
{
_expression = expression;
_provider = provider;
} public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
} System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
} public Type ElementType
{
get { return typeof(SqlQuery<T>); }
} public Expression Expression
{
get { return _expression; }
} public IQueryProvider Provider
{
get { return _provider; }
}
} public class SqlProvider<T> : IQueryProvider
{
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
IQueryable<TElement> query = new SqlQuery<TElement>(expression, this);
return query;
} public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
} public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
} public object Execute(Expression expression)
{
throw new NotImplementedException();
}
}
     public class Staff
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public DateTime? Birthday { get; set; }
public bool Deletion { get; set; }
} static void Main(string[] args)
{
IDataBase db = new DBSql();
IQueryable<Staff> query = db.Source<Staff>();
string name = "张三";
Expression express = null;
query = query.Where(x => x.Name == "赵建华");
express = query.Expression;
query = query.Where(x => x.Name == name);
express = query.Expression;
}

段点打在

public IQueryable<TElement> CreateQuery<TElement>(Expression expression)

每次query.Where都会跑这里来。并且Expression都是前后相并的结果。

到了这一步,相信大家都明白了IQueryable只存贮条件这个概念了吧。

那延迟加载呢?什么时候加载啊!当我们foreach或者ToList/ToArray时啊。这时你想到了什么?GetEnumerator()。在调用GetEnumerator()时。我们再调用Provider里的Execute(Expression)。里面解析Expression,生成SQL语句,通过反射的方式生成实例,再一个个返回回去。完成!下面我直接给代码了。解析Expression的类我也改了,这个更黄更暴力。

  public class ResolveExpression
{
public Dictionary<string, object> Argument;
public string SqlWhere;
public SqlParameter[] Paras;
private int index = 0;
/// <summary>
/// 解析lamdba,生成Sql查询条件
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public void ResolveToSql(Expression expression)
{
this.index = 0;
this.Argument = new Dictionary<string, object>();
this.SqlWhere = Resolve(expression);
this.Paras = Argument.Select(x => new SqlParameter(x.Key, x.Value)).ToArray();
} private object GetValue(Expression expression)
{
if (expression is ConstantExpression)
return (expression as ConstantExpression).Value;
if (expression is UnaryExpression)
{
UnaryExpression unary = expression as UnaryExpression;
LambdaExpression lambda = Expression.Lambda(unary.Operand);
Delegate fn = lambda.Compile();
return fn.DynamicInvoke(null);
}
if (expression is MemberExpression)
{
MemberExpression member = expression as MemberExpression;
string name = member.Member.Name;
var constant = member.Expression as ConstantExpression;
if (constant == null)
throw new Exception("取值时发生异常" + member);
return constant.Value.GetType().GetFields().First(x => x.Name == name).GetValue(constant.Value);
}
throw new Exception("无法获取值" + expression);
} private string Resolve(Expression expression)
{
if (expression is LambdaExpression)
{
LambdaExpression lambda = expression as LambdaExpression;
expression = lambda.Body;
return Resolve(expression);
}
if (expression is BinaryExpression)//解析二元运算符
{
BinaryExpression binary = expression as BinaryExpression;
if (binary.Left is MemberExpression)
{
object value = GetValue(binary.Right);
return ResolveFunc(binary.Left, value, binary.NodeType);
}
if (binary.Left is MethodCallExpression && (binary.Right is UnaryExpression || binary.Right is MemberExpression))
{
object value = GetValue(binary.Right);
return ResolveLinqToObject(binary.Left, value, binary.NodeType);
}
}
if (expression is UnaryExpression)//解析一元运算符
{
UnaryExpression unary = expression as UnaryExpression;
if (unary.Operand is MethodCallExpression)
{
return ResolveLinqToObject(unary.Operand, false);
}
if (unary.Operand is MemberExpression)
{
return ResolveFunc(unary.Operand, false, ExpressionType.Equal);
}
}
if (expression is MethodCallExpression)//解析扩展方法
{
return ResolveLinqToObject(expression, true);
}
if (expression is MemberExpression)//解析属性。。如x.Deletion
{
return ResolveFunc(expression, true, ExpressionType.Equal);
}
var body = expression as BinaryExpression;
if (body == null)
throw new Exception("无法解析" + expression);
var Operator = GetOperator(body.NodeType);
var Left = Resolve(body.Left);
var Right = Resolve(body.Right);
string Result = string.Format("({0} {1} {2})", Left, Operator, Right);
return Result;
} /// <summary>
/// 根据条件生成对应的sql查询操作符
/// </summary>
/// <param name="expressiontype"></param>
/// <returns></returns>
private string GetOperator(ExpressionType expressiontype)
{
switch (expressiontype)
{
case ExpressionType.And:
return "and";
case ExpressionType.AndAlso:
return "and";
case ExpressionType.Or:
return "or";
case ExpressionType.OrElse:
return "or";
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
default:
throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype));
}
} private string ResolveFunc(Expression left, object value, ExpressionType expressiontype)
{
string Name = (left as MemberExpression).Member.Name;
string Operator = GetOperator(expressiontype);
string Value = value.ToString();
string CompName = SetArgument(Name, Value);
string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
return Result;
} private string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)
{
var MethodCall = expression as MethodCallExpression;
var MethodName = MethodCall.Method.Name;
switch (MethodName)//这里其实还可以改成反射调用,不用写switch
{
case "Contains":
if (MethodCall.Object != null)
return Like(MethodCall);
return In(MethodCall, value);
case "Count":
return Len(MethodCall, value, expressiontype.Value);
case "LongCount":
return Len(MethodCall, value, expressiontype.Value);
default:
throw new Exception(string.Format("不支持{0}方法的查找!", MethodName));
}
} private string SetArgument(string name, string value)
{
name = "@" + name;
string temp = name;
while (Argument.ContainsKey(temp))
{
temp = name + index;
index = index + 1;
}
Argument[temp] = value;
return temp;
} private string In(MethodCallExpression expression, object isTrue)
{
var Argument1 = expression.Arguments[0];
var Argument2 = expression.Arguments[1] as MemberExpression;
var fieldValue = GetValue(Argument1);
object[] array = fieldValue as object[];
List<string> SetInPara = new List<string>();
for (int i = 0; i < array.Length; i++)
{
string Name_para = "InParameter" + i;
string Value = array[i].ToString();
string Key = SetArgument(Name_para, Value);
SetInPara.Add(Key);
}
string Name = Argument2.Member.Name;
string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
string CompName = string.Join(",", SetInPara);
string Result = string.Format("{0} {1} ({2})", Name, Operator, CompName);
return Result;
} private string Like(MethodCallExpression expression)
{
Expression argument = expression.Arguments[0];
object Temp_Vale = GetValue(argument);
string Value = string.Format("%{0}%", Temp_Vale);
string Name = (expression.Object as MemberExpression).Member.Name;
string CompName = SetArgument(Name, Value);
string Result = string.Format("{0} like {1}", Name, CompName);
return Result;
} private string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
{
object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
string Operator = GetOperator(expressiontype);
string CompName = SetArgument(Name.ToString(), value.ToString());
string Result = string.Format("len({0}){1}{2}", Name, Operator, CompName);
return Result;
} }
  public interface IDataBase
{
List<T> FindAs<T>(Expression<Func<T, bool>> lambdawhere);
int Remove<T>(Expression<Func<T, bool>> lambdawhere);
IQueryable<T> Source<T>();
}
namespace Data.DataBase
{
public class DBSql : IDataBase
{
private readonly static string ConnectionString = @"Data Source=.;Initial Catalog=btmmcms-Standard;Persist Security Info=True;User ID=sa;Password=sa;"; public IQueryable<T> Source<T>()
{
return new SqlQuery<T>();
} public List<T> FindAs<T>(Expression<Func<T, bool>> lambdawhere)
{
using (SqlConnection Conn = new SqlConnection(ConnectionString))
{
using (SqlCommand Command = new SqlCommand())
{
try
{
Command.Connection = Conn;
Conn.Open();
string sql = string.Format("select * from {0}", typeof(T).Name);
if (lambdawhere != null)
{
ResolveExpression resolve = new ResolveExpression();
resolve.ResolveToSql(lambdawhere);
sql = string.Format("{0} where {1}", sql, resolve.SqlWhere);
Command.Parameters.AddRange(resolve.Paras);
}
//为了测试,就在这里打印出sql语句了
Console.WriteLine(sql);
Command.CommandText = sql;
SqlDataReader dataReader = Command.ExecuteReader();
List<T> ListEntity = new List<T>();
while (dataReader.Read())
{
var constructor = typeof(T).GetConstructor(new Type[] { });
T Entity = (T)constructor.Invoke(null);
foreach (var item in Entity.GetType().GetProperties())
{
var value = dataReader[item.Name];
if (value == null)
continue;
if (value is DBNull)
value = null;
item.SetValue(Entity, value, null);
}
ListEntity.Add(Entity);
}
if (ListEntity.Count == 0)
return null;
return ListEntity;
}
catch (Exception ex)
{
throw ex;
}
finally
{
Conn.Close();
}
}
}
} public int Remove<T>(Expression<Func<T, bool>> lambdawhere)
{
throw new NotImplementedException();
}
} public class SqlQuery<T> : IQueryable<T>
{ private Expression _expression;
private IQueryProvider _provider; public SqlQuery()
{
_provider = new SqlProvider<T>();
_expression = Expression.Constant(this);
} public SqlQuery(Expression expression, IQueryProvider provider)
{
_expression = expression;
_provider = provider;
} public IEnumerator<T> GetEnumerator()
{
var result = _provider.Execute<List<T>>(_expression);
if (result == null)
yield break;
foreach (var item in result)
{
yield return item;
}
} System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
} public Type ElementType
{
get { return typeof(SqlQuery<T>); }
} public Expression Expression
{
get { return _expression; }
} public IQueryProvider Provider
{
get { return _provider; }
}
} public class SqlProvider<T> : IQueryProvider
{ public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
IQueryable<TElement> query = new SqlQuery<TElement>(expression, this);
return query;
} public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
} public TResult Execute<TResult>(Expression expression)
{
MethodCallExpression methodCall = expression as MethodCallExpression;
Expression<Func<T, bool>> result = null;
while (methodCall != null)
{
Expression method = methodCall.Arguments[0];
Expression lambda = methodCall.Arguments[1];
LambdaExpression right = (lambda as UnaryExpression).Operand as LambdaExpression;
if (result == null)
{
result = Expression.Lambda<Func<T, bool>>(right.Body, right.Parameters);
}
else
{
Expression left = (result as LambdaExpression).Body;
Expression temp = Expression.And(right.Body, left);
result = Expression.Lambda<Func<T, bool>>(temp, result.Parameters);
}
methodCall = method as MethodCallExpression;
}
var source = new DBSql().FindAs<T>(result);
dynamic _temp = source;
TResult t = (TResult)_temp;
return t;
} public object Execute(Expression expression)
{
throw new NotImplementedException();
}
}
}

搞定,这时可以改下数据库连接,连到自己的数据库,然后像下面这样,添加一个实体类(要与数据库表对应),就可以使用了

   class Program
{
public class Staff
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public DateTime? Birthday { get; set; }
public bool Deletion { get; set; }
} static void Main(string[] args)
{
IDataBase db = new DBSql();
IQueryable<Staff> query = db.Source<Staff>();
query = query.Where(x => x.Name == "张三");
foreach (var item in query)
{ }
}
}

是不是很简单?

虽然信息量有点大,但慢慢理清并消化,我相信会对你又很大帮助!

构建属于自己的ORM框架之二--IQueryable的奥秘的更多相关文章

  1. 抛弃EF,20分构建一个属于自己的ORM框架

    Poiuyt_cyc 博客园首页新随笔联系订阅管理随笔 - 11  文章 - 0  评论 - 111 抛弃EF,20分构建一个属于自己的ORM框架 相信EF大家都不陌生了,因为数据库表跟程序实体是一一 ...

  2. 轻量级ORM框架 QX_Frame.Bantina(二、框架使用方式介绍)

    轻量级ORM框架QX_Frame.Bantina系列讲解(开源) 一.框架简介 http://www.cnblogs.com/qixiaoyizhan/p/7417467.html 二.框架使用方式介 ...

  3. python(十二)下:ORM框架SQLAlchemy使用学习

    此出处:http://blog.csdn.net/fgf00/article/details/52949973 本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 ...

  4. c# 轻量级ORM框架 之 WhereHelper (二)

    上篇文章发布了一些设计orm框架基层的和实现,有朋友提出WhereHelper是亮点,能被认可我表示高兴. 我就把WhereHelper设计思想和代码公开下. WhereHelper 的概念就是再拼接 ...

  5. MVC系列学习(二)-初步了解ORM框架-EF

    1.新建 一个控制台项目 2.添加一个数据项 a.选择数据库 注:数据库中的表如下: b.选择EF版本 c.选择表 3.初步了解EF框架 看到了多了一个以 edmx后缀的文件 在edmx文件上,右击打 ...

  6. Golang 入门系列(十二)ORM框架gorm

    之前在已经介绍了用的github.com/go-sql-driver/mysql 访问数据库,不太了解的可以看看之前的文章 https://www.cnblogs.com/zhangweizhong/ ...

  7. “造轮运动”之 ORM框架系列(二)~ 说说我心目中的ORM框架

    ORM概念解析 首先梳理一下ORM的概念,ORM的全拼是Object Relation Mapping (对象关系映射),其中Object就是面向对象语言中的对象,本文使用的是c#语言,所以就是.ne ...

  8. 轻量级ORM框架初探-Dapper与PetaPoco的基本使用

    一.EntityFramework EF是传统的ORM框架,也是一个比较重量级的ORM框架.这里仍然使用EF的原因在于为了突出轻量级ORM框架的性能,所谓有对比才有更优的选择. 1.1 准备一张数据库 ...

  9. [Android]Android端ORM框架——RapidORM(v1.0)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4748077.html  Android上主流的ORM框架有很多 ...

随机推荐

  1. C++中的const关键字

    http://blog.csdn.net/eric_jo/article/details/4138548 C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方 ...

  2. G家二面

    题目都很基本,都属于听说过但是不会做的…都是操作系统,compiler的概念题… 概念题郁闷就郁闷在不会就是不会,就算能扯两句也会被问倒… 算法就一个,pow(x, y),5分钟不到…… 不是听说G家 ...

  3. POJ 3080 Blue Jeans (多个字符串的最长公共序列,暴力比较)

    题意:给出m个字符串,找出其中的最长公共子序列,如果相同长度的有多个,输出按字母排序中的第一个. 思路:数据小,因此枚举第一个字符串的所有子字符串s,再一个个比较,是否为其它字符串的字串.判断是否为字 ...

  4. gcc / g++ 编译选项

    g++ -Wall -m64 -W -O2 a.cpp b.cpp -o a 用下面的命令编译,生成libtriangle.so  先生成动态库 g++ -g -fpic -shared -o lib ...

  5. java基础知识回顾之java Thread类学习(十)--线程的状态以及转化使用的方法介绍

       线程的概述:         线程是程序的多个执行路径,执行调度的单位,依托于进程存在.线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间叫做线程栈,是建立线程的时候由系 ...

  6. lintcode 中等题:majority number III主元素III

    题目 主元素 III 给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的1/k. 样例 ,返回 3 注意 数组中只有唯一的主元素 挑战 要求时间复杂度为O(n),空间复杂度为O( ...

  7. lintcode :reverse integer 颠倒整数

    题目: 颠倒整数 将一个整数中的数字进行颠倒,当颠倒后的整数溢出时,返回 0 (标记为 32 位整数). 样例 给定 x = 123,返回 321 给定 x = -123,返回 -321 解题: 直接 ...

  8. 欧拉工程第66题:Diophantine equation

    题目链接 脑补知识:佩尔方差 上面说的貌似很明白,最小的i,对应最小的解 然而我理解成,一个循环的解了,然后就是搞不对,后来,仔细看+手工推导发现了问题.i从0开始变量,知道第一个满足等式的解就是最小 ...

  9. ssm框架整合小结

    1.整合思路 一.Dao层:整合mybatis和spring 需要的jar包: 1.mybatis的jar包 2.Mysql数据库驱动 3.数据库连接池 4.Mybatis和spring的整合包. 5 ...

  10. 想要风投被你的融资 PPT 打动吗?别忘了你其实就是在想方设法卖出自己公司的部分股权

      硅谷,一个常常见诸于报端,看着很熟悉,但是又不那么被人所了解的未及之地.它不是一个严格限定的地理位置,一般来说是指旧金山和湾区,其中湾区又分为东湾(East Bay)和南湾(South Bay), ...