之前写过一篇《统一的仓储接口》,为了方便使用不同的仓储。在我们的项目中使用的是EF4.0,但是这个版本的EF有一些性能问题没有解决,又不想升级到EF6,具体EF6有没有解决暂时不清楚。我们的项目之前运行的都不错,突然一天数据库服务器CPU 100%,IIS服务器CPU又正常,过几个小时之后又恢复正常,每个星期一早上都这样,可以肯定就是用户同时操作并发过多造成,查找之后,是一个表的数据被锁住。报错was deadlocked on lock,解决办法就是查询sql上加上 with nolock,但是EF不支持,但有不想放弃linq to sql的优势,无奈只能自己实现,方案是之前已有功能并发不多的地方保持不变,依然使用EF,在并发多的地方使用自己实现的linq to sql。

目前并没有完整实现linq to sql,只是实现了单表的情况,对于有关联引用由于时间限制并未实现。

实现仓储,需要实现两个对象,[IObjectContext、IOrderedQueryable]

上篇说到的上下文对象IObjectContext

public interface IObjectContext:IDisposable{string Name { get; set; }IDbConnection CreateConnection();bool SaveChanges();}

考虑到项目中有些特殊情况需要使用sql,所以增加了CreateConnection方法返回一个IDbConnection,当然在逼不得已的情况下使用。

DbContext的实现比较简单,重点管理一下连接池,配置,大概如下,具体的实现由于篇幅省略,源码会在篇尾附上。

public class DbContext : IDisposable, IObjectContext{public EntityStateManager Manager { get; private set; }public string DbType { get; private set; }public string ConnectionString { get; set; }public int PoolSize { get; set; }public bool AllowUpdateWithNoExp { get; set; }public bool AllowDeleteWithNoExp { get; set; }List<System.Data.IDbConnection> connPool = new List<System.Data.IDbConnection>();public DbContext();public DbContext(string connectionString, string dbType);public DbContext(string connectionString, string dbType, bool allowUpdateWithNoExp, bool allowDeleteWithNoExp);void InitContext(string connectionString, string dbType, int poolSize, bool allowUpdateWithNoExp, bool allowDeleteWithNoExp);public EntitySet<T> GetEntitySet<T>(string tableName = null, bool noLock = false, bool noTracking = false);public int SubmitChange();void IDisposable.Dispose();string IObjectContext.Name { get; set; }bool IObjectContext.SaveChanges();public System.Data.IDbConnection CreateConnection();System.Data.IDbConnection IObjectContext.CreateConnection();

另一个对象则是仓储EntitySet<T>,大概代码如下

public class EntitySet<T> : IOrderedQueryable<T>{public DbContext Context { get; private set; }string TableName { get; set; }bool NoLock { get; set; }bool NoTracking { get; set; }DbQueryProvider MyProvider { get; set; }Expression MyExpression { get; set; }public EntitySet(string tableName, bool noLock, bool noTracking, DbContext context);public EntitySet(string tableName, bool noLock, bool noTracking, Expression expression, DbContext context);void InitEntitySet(string tableName, bool noLock, bool noTracking, Expression expression, DbContext context);public Expression Expression{get { return this.MyExpression; }}public Type ElementType{get { return typeof(T); }}public IQueryProvider Provider{get { return this.MyProvider; }}public IEnumerator<T> GetEnumerator();IEnumerator IEnumerable.GetEnumerator();void MyProvider_OnExecuted(object sender, EventArgs e);void TrackEntity(object result);void notifyT_PropertyChanged(object sender, PropertyChangedEventArgs e);public void Add(T item, bool INSERT_IDENTITY = false);public void Update(Expression<Func<T, bool>> exp, object obj);public void Remove(T item);public void Remove(Expression<Func<T, bool>> exp);

在仓储里面我增加了我需要的东西,比如Add可以插入标识,Update可以根据表达式更新对象,而不需要把所需要的对象先取出来,修改再保存,数据量大的时候EF性能有问题,Remove也同样如此,为了防止误操作,所以在之前DbContext中增加了配置,是否允许无条件删除、更新数据。另外一个重点也就是增加nolock支持,当然这个在生成sql的时候加上就行,非常简单。

说到这里其实只不过是个大概,这里面的操作无非就是四种CRUD。我设计了四个Command来解决InsertCommand、UpdateCommand、SelectCommand、DeleteCommand,他们都继承EntityCommand只要实现一个方法public abstract int Execute(IDbConnection conn,IDbTransaction tran);

到这里其实InsertCommand、UpdateCommand、DeleteCommand实现都非常简单,因为有了实现SelectCommand的基础代码,解析Expression就简单的多了,可以说解析Expression才是整个的关键。

解析的代码没有那么多复杂的东西,我个人的原则就是尽量简单,不为了追求设计而增加多余的东西,虽然我对设计模式也很痴迷。

上图就是解析的全部代码,这其中代码其实并不重要,重要的是解决的思路,这里面有两个重要的对象QueryExpressionClosure(查询表达式闭包)、QueryCommnClosure(查询通用闭包)。

我们知道IQueryable其实就是expression tree,我记得以前很早的时候有人实现解析表达式的时候,是一边解析一边生产sql,这样的做法非常不科学,会造成很多不必要的sql闭包,

一个简单的查询:比如q.Where(c=>c.Age>20).OrderBy(c=>c.Id);就会生成最少三个闭包大概sql是这样 select xxx from (select * from (select xxx from tablex) as t1 where t1.Age>20) as t2 order by t2.Id。但是正常来说应该是select xxx from tablex where Age>20 order by Id。

所以正确的应该是先将表达式树解析成表达式闭包,在将表达式闭包解析成sql。

如何将表达式解析成表达式闭包?

稍微分析一下就可以看出,当遇到take、SKIP、sum、max、min、average、any、contains、distinct、first、firstordefault、longcount、count的时候就是另外一个sql闭包了,

大概代码如下:

protected override Expression VisitMethodCall(MethodCallExpression m){var methodName = m.Method.Name.ToLower();if (this.CurrQueryExpressionClosure == null && this.QueryExpressionClosures.Count == 0 && Utility.ToListMethods.Contains(methodName)){this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = "tolist";this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);}switch (methodName){case "orderbydescending":case "orderby":case "thenby":case "thenbydescending":{if (!Utility.IgnoreOrderByMethods.Contains(this.CurrQueryExpressionClosure.MethodName)){this.CurrQueryExpressionClosure.OrderByExpressions.Add(m);}break;}case "groupby":{this.CurrQueryExpressionClosure.GroupByExpressions.Add(m.Arguments[1]);break;}case "where":{var l = ((m.Arguments[1] as UnaryExpression).Operand as LambdaExpression);this.CurrQueryExpressionClosure.WhereExpressions.Add(l);break;}case "take":{if (this.CurrQueryExpressionClosure.MethodName != "skip"){this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);this.CurrQueryExpressionClosure.MethodName = methodName;}this.CurrQueryExpressionClosure.Take = System.Convert.ToInt32((m.Arguments[1] as ConstantExpression).Value);break;}case "skip":{if (this.CurrQueryExpressionClosure.MethodName != "take"){this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);this.CurrQueryExpressionClosure.MethodName = methodName;}this.CurrQueryExpressionClosure.Skip += System.Convert.ToInt32((m.Arguments[1] as ConstantExpression).Value);break;}case "sum":case "max":case "min":case "average":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);if (m.Arguments.Count > 1){this.CurrQueryExpressionClosure.EvalNumericExpression = m.Arguments[1];}break;}case "any":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);if (m.Arguments.Count > 1){var l = ((m.Arguments[1] as UnaryExpression).Operand as LambdaExpression);this.CurrQueryExpressionClosure.WhereExpressions.Add(l);}break;}case "contains":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);if (m.Arguments.Count > 1){this.CurrQueryExpressionClosure.WhereExpressions.Add(m.Arguments[1]);}break;}case "distinct":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);break;}case "select":{var lambdaExp = ((m.Arguments[1] as UnaryExpression).Operand as LambdaExpression);this.CurrQueryExpressionClosure.QuerySelectExpressions.Add(lambdaExp);break;}case "first":case "firstordefault":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.CurrQueryExpressionClosure.Take = 1;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);if (m.Arguments.Count > 1){this.CurrQueryExpressionClosure.WhereExpressions.Add(m.Arguments[1]);}break;}case "longcount":case "count":{this.CurrQueryExpressionClosure = new QueryExpressionClosure();this.CurrQueryExpressionClosure.MethodName = methodName;this.QueryExpressionClosures.Add(this.CurrQueryExpressionClosure);if (m.Arguments.Count > 1){this.CurrQueryExpressionClosure.WhereExpressions.Add(m.Arguments[1]);}break;}}return base.VisitMethodCall(m);}

有了表达式闭包之后,这个时候理解起来就清晰多了,就可以通过一个ParserContext梳理一遍表达式闭包,生成一个通用闭包,并且得到需要的信息。

通用闭包大概:

public class QueryCommnClosure{public int Take { get; set; }public int Skip { get; set; }public bool NoLock { get; set; }public string MethodName { get; set; }public string TableName { get; set; }public List<string> SelectColumns { get; set; }//public List<SelectTypeConstructor> SelectTypes { get; set; }public Dictionary<string, string> OrderBys { get; set; }public List<string> GroupBys { get; set; }public List<string> WhereSQLs { get; set; }public string EvalNumericSQL { get; set; }public string TableAlias { get; set; }public QueryCommnClosure(){this.SelectColumns = new List<string>();//this.SelectTypes = new List<SelectTypeConstructor>();this.OrderBys = new Dictionary<string, string>();this.GroupBys = new List<string>();this.WhereSQLs = new List<string>();}public void Generate(ParserContext context){...篇幅限制省略}}

另外一个元数据,其实这个非常简单,我为了灵活,支持解析EF的edmx(msl、csdl)、Attribute(松散灵活的,实体上可以加Attribute,也可以不加)两种。有了这个元数据就可以做到实体、表的映射。

源码下载

人快30了,成家却未能立业,做了一年多的项目因为省领导政策的原因失败,说实话干这个行当不知道对不对,可能是有着一张不老的脸,在别人眼里,都以为是才24、5岁,对我也是不够信任,但是实际干起来别人才知道我实力如何,但老板不知道。总是干的最多,拿的只能算个一般,呵呵...。

昆明有看上俺的可以联系下我,求出路,目前公司也不是说要倒闭什么的,其实也很稳定,但是这个项目完完了,另一个稳定gps是其他人做的,感觉在公司已经多余了,工资也不是看涨的样子,毕竟要买房,养家糊口。

转载于:https://www.cnblogs.com/bmrxntfj/p/3434953.html

实现自己的Linq to Sql相关推荐

  1. 如何查看Linq to SQL运行时,实际执行的Sql语句

    调试Linq to sql代码是, 如果遇到错误,很难判断错误的原因是什么,如果能够输出实际执行的sql原文,对于我们寻找错误的原因有有很大帮助. 以下是我用到的方法: StringBuilder s ...

  2. Linq to SQL 资源

    Scott Guthrie 的 Linq to SQL 系列: 1)介绍 http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to ...

  3. .NET 3.5(12) - DLINQ(LINQ to SQL)之事务处理和并发处理

    步步为营VS 2008 + .NET 3.5(12) - DLINQ(LINQ to SQL)之事务处理和并发处理 作者:webabcd 介绍 以Northwind为示例数据库,DLINQ(LINQ ...

  4. 一步一步学Linq to sql(六):探究特性

      延迟执行 IQueryable query = from c in ctx.Customers select c; 这样的查询句法不会导致语句立即执行,它仅仅是一个描述,对应一个SQL.仅仅在需要 ...

  5. LINQ to SQL 在 Visual Studio 2008 中的简单应用

    在.Net Framework 3.5 中,最激动人心的就是增加了LINQ功能,LINQ在数据集成的基础上提供了新的轻型方式.有了LINQ,我们创建的查询现在就编程了.Net 框架的一个成员,在对要操 ...

  6. 一步一步学Linq to sql(一):预备知识

    从今天起将推出新手讲堂,首先从linq开始详细讲解.一步一步学Linq to sql(一):预备知识 什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集 ...

  7. 一步一步学linq to sql(四)查询句法

    select 描述:查询顾客的公司名.地址信息 查询句法: var 构建匿名类型1 = from c in ctx.Customers select new { 公司名 = c.CompanyName ...

  8. 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统 (Part 1)

    在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统 (Part 1) 在新的.Net Framework 3.5平台上,Microsoft发布了LINQ(C# 3.0, ...

  9. 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(Part5) 转

    设计开发表现层 表现层负责应用程序的用户界面,图4.1是表现层的详细视图,包括Web Forms, Web User Control, Code-Behind 和共享的用户界面代码.表现层负责推送信息 ...

  10. LINQ : 如何为LINQ TO SQL实现自定义业务逻辑

    LINQ TO SQL很好很强大,它几乎包含了我们能够想到的所有与数据库有关的操作,甚至也包含了一些我们可能都没有想到的. 但不管怎样,也许我们需要在LINQ TO SQL的操作中添加一个自定义业务逻 ...

最新文章

  1. 神经网络变成小怪兽,还能互相 battle!这款游戏用最简单的方式训练神经网络...
  2. 创建一个显示所有预定义WPF颜色的ListBox
  3. bash: ifconfig: command not found
  4. esp32 烧录固件
  5. cs模式下,显示网络图片一例
  6. oracle之set运算符和练习
  7. ios 数组中的字典排序_利用数组和字典,实现按指定规则的排序
  8. 《Reids 设计与实现》第一章 简单动态字符串和链表
  9. 【高校宿舍管理系统】第三章 Layui整合Axios
  10. linux5 yum安装,(5)Linux_软件管理_yum安装本地软件
  11. 8月9日 华为发布了其自研的鸿蒙操作系统,华为正式发布自研操作系统鸿蒙,面向AI全场景...
  12. C++ 运算符重载四(自定义数组类)
  13. 撞库、脱库和洗库是什么意思
  14. [verilog] 八位比较器
  15. 改进left函数,截取varchar需要的字符串长度
  16. 风行python_Python曾是程序员的“瑞士军刀”,而如今正被慢慢取代
  17. 孤军大作战!疯狂DIY 1U硬件防火墙实录(转)
  18. JAVA类运行时,报错“Error occurred during initialization of boot layer”
  19. Linux系统信号定义
  20. linux-文件服务

热门文章

  1. 用户体验是非常难琢磨的东西
  2. Ubuntu16.04安装chrome浏览器
  3. python向it新增5个元素_Python序列、元组、列表、集合及字典笔记整理
  4. C#之多态与继承汽车类
  5. java jpeg压缩算法_在创建JPEG时,我可以更改Java的ImageWriter使用的压缩算法吗?
  6. datagrip替换字_Datagrip 快捷键和常用插件持续更新一集一些使用技巧
  7. 2021教师资格证中学科目二简答汇总分享
  8. Selenium自动化测试-4.By定位及如何确定元素唯一
  9. Python 中的 if __name__ == ‘__main__‘ 该如何理解
  10. TokenInsight:反映区块链行业整体表现的TI指数较昨日同期上涨0.8%