LinqToDB 源码分析——前言

记得笔者进入公司的时候接触的第一个ORM框架是Entity Framework。为了Entity Framework也看了不些的英文资料(不是笔者装B哦)。正式使用三个月后。笔者对他有一个全面性的认识。我只能说他真的很强大,也很方便。可是我并不是很喜欢他。要问为什么的话,笔者只能说喜欢就是喜欢。不喜欢就是不喜欢。不需要过多的理由。笔者就是这样子的一个人。但是笔者不会忽略他的强大的一面。微软的目标还是老样子——开发简单化。只是在Entity Framework的数据迁移上面笔者不是很喜欢。至少在笔者团队开发过程常常会出现因版本不对导至数据丢失。不管如何笔者对Entity Framework的使用也至少有一年的时间。由于项目以领域驱动(DDD)为核心思想。所以在设计的时候,会用到一些笔者觉得还不错的思想。比如工作单元(Unit Of Work模式)。Entity Framework在早期的时候是不开源码。笔者以前是从事JAVA开发的。这对笔者来讲心态上有一点不能接受(当然这也是笔者个人心态)。终于Entity Framework6开源码了。如果有兴趣的朋友可以下载下来看看(源码地址:https://github.com/aspnet/EntityFramework6)。

第二年时候笔者接触了第二个ORM框架是LinqToDB。笔者不是想强调LinqToDB有多么好。笔者只是觉得他是一个相当不错的开源ORM框架。功能不比EF差,用法上很接近的EF,却比EF来得轻量,而且又多出了自己的特色。所以如果你用EF用得有一点烦了或是觉得EF有一点笨重。想去看看有没有别的ORM框架。不烦试试LinqToDB。

开发环境



对于LinqToDB的dll包在NuGet上可以下载到。只要输入“linq2db”即可。同时也可以在Github上面下载(https://github.com/linq2db/linq2db)。最好选择跟笔者一样子的版本,比较稳定。如下

软件开发工具:Visual Studio 2013

LinqToDB版本:linq2db-Release.1.0.7.4

数据库:SQL Server 2008R

LinqToDB介绍



LinqToDB做为一个轻量级的ORM框架。当然可以让开发人员用面向对象的思想来操作数据库。而且他基于是Linq上面进行开发的。所以一般的Linq操作他也是支持的。同时作者又扩展对应的DML和DDL。比如增加 Insert, Delete, Update, CreateTable, DropTable等方法。相对于EF来讲,LinqToDB显得还是很弱小,没有那么强大。LinqToDB可以说只是把Linq动作变成对应的SQL语句。然后在进行操作数据库。这显然更加接近原生态的做法。也是笔者为什么喜欢的点之一。那么LinqToDB到底能支持多少种数据库。作者在Github上也做也明确指出来。如下。

LinqToDB是如何使用呢?作者在Github上面用了经典的Northwind数据库来讲解。不如笔者也来用一下Northwind数据库进行讲解本系列的一些试验和列子。Northwind数据库是Sql Server 2000数据库的经典设计的数据库。如果不懂的朋友,请百度一下。我们都知道EF有三种模式开发。那么是不是意味着LinqToDB也有可能有这三种开发呢?对于这一点作者也没有很明确的说明。LinqToDB并没有像EF那样子可以根据设计好的类来生成对应的数据库表结构。只能说目前LinqToDB有俩种方式来进行开发——一种原生态的代码,一种根据TT模板。原生态的代码就是数据库建完之后,配置对应的映射,然后自己业务操作。根据TT模板就是用TT模板生成数据库对象映射。这俩种方式笔者会更加的喜欢前一种。

LinqToDB和EF有一个相类似点。他们都有一个关键的类。这个类拉动了所有动作的上下文。如果说DbContext类是EF的核心,那么DataContext类便是LinqToDB的重心。DataContext类的作用跟DbContext类在EF框架里面的作用很接近。使得只有用过EF的人对于LinqToDB有一种亲近感。

 1 using LinqToDB;
 2 using LinqToDB.DataProvider.SqlServer;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7
 8 namespace LinqToDBExample
 9 {
10     public class DbNorthwind:DataContext
11     {
12         public DbNorthwind()
13             : base(SqlServerTools.GetDataProvider(SqlServerVersion.v2008), "Data Source=.;Initial Catalog=Northwind;User ID=sa;Password=123")
14         {
15
16         }
17
18         public ITable<Products> Products { get { return this.GetTable<Products>(); } }
19
20     }
21 }

笔者新建一个类DbNorthwind。让这个类继承DataContext类。同时增加了获得Northwind数据库中dbo.Products表对应的关系类属性Products。这个属性是一个ITable接口。当然跟EF的IDbSet接口有一曲同工之妙。是不是觉得像EF找到了失散多年的兄弟。而对映射配置上那些事情只怕看完之后,基本上你会跟笔者一样子淡定了许多。也许你很少用到EF的注解配置,但是这不能代表他们俩者之间不存在相似之处。

 1 using LinqToDB.Mapping;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6
 7 namespace LinqToDBExample
 8 {
 9     public class Products
10     {
11         [PrimaryKey, Identity]
12         public int ProductID { set; get; }
13
14         [Column(Name = "ProductName"), NotNull]
15         public string Name { get; set; }
16     }
17 }

看到了吧。可以说没有什么新的知识。即使没有用过EF,也可以从关键字中得到对应的信息。笔者也相信关键字PrimaryKey你在Sql Server中一定会有用到过。即是主键的意思。这时候来一个简单的查询应该是一件非常棒的事情。可以让我们看到他在查询是如何的表现。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5
 6 namespace LinqToDBExample
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             using(var db = new DbNorthwind())
13             {
14                 var query = from p in db.Products
15                             where p.ProductID > 25
16                             orderby p.Name descending
17                             select p;
18
19                 foreach (Products product in query.ToList())
20                 {
21                     Console.WriteLine(string.Format("ProductID:{0} ------------ProductName:{1}", product.ProductID, product.Name));
22                 }
23
24             }
25
26             Console.ReadKey();
27         }
28     }
29 }

执行结果:

显然,LinqToDB在使用的语法跟EF很接近。正如笔者所讲的——如果你真的不太喜欢EF的话,可以试着用一下LinqToDB吧。也许你可以看到另一片天空也说不一定。

如果你觉得上面DataContext类的构造函数用法有一点烦。不担心让笔者在介绍一种。通常我们在开发的时候会用到App.config或是Web.config。而对于connectionStrings节点相信大家并不陌生。笔者要介绍这种便是使用配置文件来完成。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="linq2db" type="LinqToDB.Configuration.LinqToDBSection, linq2db" requirePermission="false" />
  </configSections>
  <linq2db defaultConfiguration="Aomi" />
  <connectionStrings>
    <add name="Aomi"   connectionString="Data Source=.;Initial Catalog=Northwind;User ID=sa;Password=123" providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

LinqToDB自己定义一个配置类。正如上面看到的一样子。我们只要设置下面一段配置就可以了。相信笔者应该能看懂吧。就是指定默认的连接。而对于connectionStrings节点的使用别让笔者费神了吧。

<linq2db defaultConfiguration="Aomi" />

配置好了上面的信息。对应的继承DataContext类的子类就可以不必须去设置对应的连接字符串了。对应的代码如下。

 1 using LinqToDB;
 2 using LinqToDB.DataProvider.SqlServer;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7
 8 namespace LinqToDBExample
 9 {
10     public class DbNorthwind:DataContext
11     {
12         public ITable<Products> Products { get { return this.GetTable<Products>(); } }
13
14     }
15 }

事实上这种用配置的方法在EF里面也存在,而上面方法就是通过传入参数来确定对应的连接字符串。只不过上面在配置文件就指定了默认的连接。所以我们可以不用指定默认的,用直接指定的方式来获得连接。如下

 1 using LinqToDB;
 2 using LinqToDB.DataProvider.SqlServer;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7
 8 namespace LinqToDBExample
 9 {
10     public class DbNorthwind:DataContext
11     {
12         public DbNorthwind()
13             : base("Aomi")
14         { }
15         public ITable<Products> Products { get { return this.GetTable<Products>(); } }
16
17     }
18 }

如果大家还是不满意的话,笔者也只能跪求解脱——因为笔者只知道这三种方式了。

LinqToDB的增加功能

对于EF相信大家都知道没有什么增加不增加的概念。至少笔者是这样子认为的。因为笔者没有看到对应的增加方法。很多人说难道对一个同步数据库的集合表进行增加数据就不算增加吗(这里集合表就是从IDbSet中得来的集合)。笔者认不是。EF好像只有数据有没有发生变化这个概念。你对集合表操作就表现了数据发现变化。最后提交变化的时候,EF会知道原来增加了。形式上有一点像是在用DataSet进行数据库。而LinqToDB却不是这样子,他还是有增加这个概念了。让笔者举一些列子吧。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15                 Products product = new Products();
16                 product.Name = "aomi";
17
18                 db.Insert(product);
19             }
20
21             Console.ReadKey();
22         }
23     }
24 }

执行结果:

上面做了一个简单的操作。只是可惜LinqToDB的增加功能有一点让笔者失望。他并没有像EF那么样子。增加的时候,如果主键是自动增加(标识),那么增加成功之后会把主键同步到增加对象中。如图下,增加成之后我们看到对象ProductID还是0。

对于这一点LinqToDB到是用了一种传统的方式。扩展了一叫InsertWithIdentity方法。增加成之后返回对象主键。所以相对于上面想要把增加成之后的主建同步到对应增加对象的属性中就必须用传统的方式。笔者真的一点受不了。修改如下。

 product.ProductID = Convert.ToInt32(db.InsertWithIdentity(product));

虽然上面的增加功能让笔者一时难以接受。不过作者在增加方面还扩展不少方法。让我们可以直接在对应的表属性上面操作。即是ITable接口类型的属性。这个时候只要设置对象属性值就可以了。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15                 db.Products.Value(t => t.Name, "aomi1")
16                     .Insert();
17             }
18
19             Console.ReadKey();
20         }
21     }
22 }

看到了吧。上面由于笔者的Products类只写了一个属性。ProductID属性又是自动增加。所以只有一个Value。如果要对多个属性设置,就可以在Value在点Value。最后在点Insert或是InsertWithIdentity。

LinqToDB的更新功能

看完了LinqToDB的增加功能之后。不知道大家对LinqToDB有没有一种想要去了解冲动呢?没事,让我们看一下LinqToDB对更新方面,又做了一些什么变化。不,不能说什么变化。应该说做了什么设计。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15
16                 Products product = new Products();
17                 product.ProductID = 79;
18                 product.Name = "aomi update";
19
20                 db.Update(product);
21
22             }
23
24             Console.ReadKey();
25         }
26     }
27 }

上面这段代码做的事情很简单——更新ProductID为79的数据的Name值。执行成功并且更新数据库。你敢信。笔者记得当场傻眼。过用EF的开发人员都知道如果我们新建一个普通的对象。注意不是从数据库里面Linq出来的。而是用new关键字新建的。这时候你意图更新但是EF却会变成增加。可是LinqToDB却不存在这样子的问题。这设计笔者给99分。1分是笔者不认同——感觉设计思想有一点乱来。早的时候笔者一直希望看到LinqToDB在操作对象上能像EF或Hibernate那样子——存在对象状态的说法。从增加的时候主键不能同步到现在新建对象更新成来看LinqToDB并没有对象持久化这个说法。也罢,必竟是一个轻量级的ORM框架。

上面的更新做法相信大家一定不会有什么烦感。如果新建的对象设置主键的值之后,都能更新。那么从数据库Linq出来的数据更不用讲了。主要是要看看一下LinqToDB给我们带来另一个更新的做法。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15                 db.Products
16                     .Where(t => t.ProductID == 83)
17                     .Set(t => t.Name, "Update aomi")
18                     .Update();
19             }
20
21             Console.ReadKey();
22         }
23     }
24 }

由于增加功能里面的Value静态扩展方法出现,导至笔者对更新功能的Set静态扩展方法出现一点也不奇怪。如果要更新多个属性的值,只要是点多个Set就可以了。当然最后不要忘记了点Update。更新功能的扩展方法笔者还是比较喜欢的。相对于EF来讲,EF必须先到数据库获得要更新的对象。然后在修改对象的属性值才能更新。而对于LinqToDB来讲,只要一步到位。

LinqToDB的删除功能

看完了增加和更新,显然不能忘了看一下LinqToDB在删除方面是如何做的。正如上面所讲的LinqToDB并没有对象状态这一个说法。所以下面的代码删除成功了也不会觉得奇怪。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15                 Products product = new Products();
16                 product.ProductID = 79;
17
18                 db.Delete(product);
19             }
20
21             Console.ReadKey();
22         }
23     }
24 }

另一种做法:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6
 7 namespace LinqToDBExample
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using(var db = new DbNorthwind())
14             {
15                 db.Products.Where(t => t.ProductID == 83).Delete();
16             }
17
18             Console.ReadKey();
19         }
20     }
21 }

上面的俩种做法。笔者很淡定的飘过。不过到是说明了一个问题——增删改一般都有俩种方式。一种是从IDataContext接口上扩展出来的方法。一种是从IQueryable接口或是ITable接口扩展出来的方法。

在开发一个大型项目的时候,对于多表操作显得家常便饭。所以每一个框架都有自己处理事务的功能。同样子LinqToDB也实现了事务功能。不过他的事务让笔者很舒服,不是因为他有多么好。而是他更加接近原生态。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6 using LinqToDB.Data;
 7
 8 namespace LinqToDBExample
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             using(var db = new DbNorthwind())
15             {
16                 using (DataContextTransaction transaction = db.BeginTransaction())
17                 {
18                     try
19                     {
20                         int deleteID = Convert.ToInt32(db.Products.Value(t => t.Name, "aomi79").InsertWithIdentity());
21                         db.Products.Where(t => t.ProductID == 79).Delete();
22                     }
23                     catch (Exception ex)
24                     {
25                     }
26                 }
27
28             }
29
30             Console.ReadKey();
31         }
32     }
33 }

上面的做法用的是自动提交事务,如果你想要自己提交的事务的话,一定要设置他的参数。默认事务是自动提交事务的。如下,笔者关闭掉自动提交事务。变成手动提交了。所以传入false。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using LinqToDB;
 6 using LinqToDB.Data;
 7
 8 namespace LinqToDBExample
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             using(var db = new DbNorthwind())
15             {
16                 using (DataContextTransaction transaction = db.BeginTransaction(false))
17                 {
18                         db.Products.Value(t => t.Name, "aomi83").Insert();
19                         int deleteID = Convert.ToInt32(db.Products.Value(t => t.Name, "aomi79").InsertWithIdentity());
20                         int count = db.Products.Where(t => t.ProductID == deleteID).Delete();
21
22                         if (count <= 0)
23                         {
24                             transaction.RollbackTransaction();
25                         }
26                         else
27                         {
28                             transaction.CommitTransaction();
29                         }
30                 }
31
32             }
33
34             Console.ReadKey();
35         }
36     }
37 }

结束语



LinqToDB框架有他自己的优点和特色。同样子也有他的不足。而笔者想为大家介绍LinqToDB的使用的同时更想为介绍大家他是什么实现的。如果本系列中有出现错误的说法,希望大家能谅解。可以的话,请提出来让笔者也学习一下。谢谢。

时间: 2024-10-11 01:48:52

LinqToDB 源码分析——前言的相关文章

LinqToDB 源码分析——生成表达式树

当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开IQueryProvider接口.LinqToDB框架跟这俩个接口有关系的有三个类:Table<T>类.ExpressionQuery<T>类.ExpressionQueryImpl<T>类.其中最重要的是ExpressionQuery<T>类.他是Table&l

LinqToDB 源码分析——生成与执行SQL语句

生成SQL语句的功能可以算是LinqToDB框架的最后一步.从上一章中我们可以知道处理完表达式树之后,相关生成SQL信息会被保存在一个叫SelectQuery类的实例.有了这个实例我们就可以生成对应的SQL语句.想要了解这一步部分的功能就必须从三个方面入手.一.Linq To SQL的机制原理.二.如何生成SQL语句.三.设置映射结果. 生成映射表达式 对于Linq To SQL的机制原理在前面的章节里面已经讲过了.这里笔者提出来主要目标是明确什么时候触发.下面的代码不是看前面的获得Query<

libuv源码分析前言

Libevent,libev,libuv三者的区别所在? libevent提供了全套解决方案(事件库,非阻塞IO库,http库,DNS客户端),然而libevent使用全局变量,导致非线程安全.它的watcher结构也过大,把I/O.计时器.信号句柄整合在一起.而且(作者认为)libevent的附加组件如http和dns库都实现不好,且有安全问题. libev因libevent而诞生,对libevent做了改进,避免使用全局变量,拆分watcher等.另外libev去掉了外部库(比如http和d

LinqToDB 源码分析——设计原理

我们知道实现了IQueryable<T>接口和IQueryProvider接口就可以使用Linq To SQL的功能.关于如何去实现的话,上一章也为我们引导了一个方向.LinqToDB框架也是顺着这个方向进行的.然而笔者对LinqToDB框架的作者真的很无语.如果有打开过LinqToDB框架源码的朋友,可能会发现很多代码都没有文字说明.这无疑给那些想要深入了解框架的人加大了前进力度.本来笔者以为只是没有相关代码说明不用怕.只要找到对应的文档应该没有什么大问题.于是笔者也跟很多人一样子--去作者

Android之App界面的挂载与显示及源码分析

前言 入门 视图树 源码分析 前言 好久没有写博客了,都感觉有些生疏了. 总觉的人对自己要求高一些比较好,这样才进步比较快.接下来会继续给大家带来一些更有用的知识. 个人水平有限,如果感觉我的博客对您有用处,那就留个言给下鼓励:如果那里写的有误,请各位看客老爷多多拍砖! 注意:此处我使用的IDE是Android Studio 入门 相信每个人在学习Android时,都创建过很多Demo工程,那么下面的代码,大家一定非常眼熟了. public class MainActivity extends

jquery源码分析之一前言篇

1.问:jquery源码分析的版本是什么? 答:v3.2.1 2.问:为什么要分析jquery源码? 答:javascript是一切js框架的基础,jquery.es6.vue.angular.react.redux,node.只有完全掌握原生js,你才能快速理解各种js框架,也是你达到高级程序员的必经之路.记得我刚来东航电商工作的时候,一位高级程序员跟我说过,中级前端就是往上走,学习掌握各种流行js框架,但是想往高级走,我们得往下走,对于底层语言的熟悉,懂得它的思想才能在程序领悟拥有创造力,只

Spring boot 源码分析(前言)

开坑达人 & 断更达人的我又回来了 翻译的坑还没填完,这次再开个新坑= = 嗯,spring boot的源码分析 本坑不打算教你怎么用spring boot = = 也不打算跟你讲这玩意多方便 =  = 咱们就看代码= = 然后呢... 任何源码分析总要有个开始 而本坑,我打算从run方法开始 SpringApplication.run(Main.class, args); 然后碰见什么讲什么. 嗯就酱紫. 另外,老规矩: 更新看心情,回车看心情 欢迎催更,欢迎批评指正 嗯就酱紫吧. 下一章讲S

【集合框架】JDK1.8源码分析之Collections &amp;&amp; Arrays(十)

一.前言 整个集合框架的常用类我们已经分析完成了,但是还有两个工具类我们还没有进行分析.可以说,这两个工具类对于我们操作集合时相当有用,下面进行分析. 二.Collections源码分析 2.1 类的属性   2.2 构造函数 private Collections() { } 说明:私有构造函数,在类外无法调用. 2.3 方法分析 下面是Collections的所有方法. 可以看到,Collections的方法包含了各种各样的操作.下面分析最常用的方法. 1. sort函数 该函数有两个重载函

Android逆向之旅---反编译利器Apktool和Jadx源码分析以及错误纠正

一.前言 在之前的破解过程中可以看到我们唯一离不开的一个神器那就是apktool了,这个工具多强大就不多说了,但是如果没有他我们没法涉及到后面的破解工作了,这个工具是开源的,也是使用Java语言开发的,代码相对简单,我们今天就来分析一下他的大体逻辑,注意是大体逻辑哦,因为如果要一行一行代码分析,首先觉得没必要,其次浪费时间,有了源码,谁看不懂呢.至于为什么要分析这个工具其实原因只有一个,就是我们在之前的反编译过程中会发现,总是有那么几个apk应用不让我们那么容易的反编译,他们就利用apktool