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有一种亲近感。
using LinqToDB;
using LinqToDB.DataProvider.SqlServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LinqToDBExample
{
public class DbNorthwind:DataContext
{
public DbNorthwind()
: base(SqlServerTools.GetDataProvider(SqlServerVersion.v2008), "Data Source=.;Initial Catalog=Northwind;User ID=sa;Password=123")
{ } public ITable<Products> Products { get { return this.GetTable<Products>(); } } }
}
笔者新建一个类DbNorthwind。让这个类继承DataContext类。同时增加了获得Northwind数据库中dbo.Products表对应的关系类属性Products。这个属性是一个ITable接口。当然跟EF的IDbSet接口有一曲同工之妙。是不是觉得像EF找到了失散多年的兄弟。而对映射配置上那些事情只怕看完之后,基本上你会跟笔者一样子淡定了许多。也许你很少用到EF的注解配置,但是这不能代表他们俩者之间不存在相似之处。
using LinqToDB.Mapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LinqToDBExample
{
public class Products
{
[PrimaryKey, Identity]
public int ProductID { set; get; } [Column(Name = "ProductName"), NotNull]
public string Name { get; set; }
}
}
看到了吧。可以说没有什么新的知识。即使没有用过EF,也可以从关键字中得到对应的信息。笔者也相信关键字PrimaryKey你在Sql Server中一定会有用到过。即是主键的意思。这时候来一个简单的查询应该是一件非常棒的事情。可以让我们看到他在查询是如何的表现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
var query = from p in db.Products
where p.ProductID >
orderby p.Name descending
select p; foreach (Products product in query.ToList())
{
Console.WriteLine(string.Format("ProductID:{0} ------------ProductName:{1}", product.ProductID, product.Name));
} } Console.ReadKey();
}
}
}
执行结果:
显然,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类的子类就可以不必须去设置对应的连接字符串了。对应的代码如下。
using LinqToDB;
using LinqToDB.DataProvider.SqlServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LinqToDBExample
{
public class DbNorthwind:DataContext
{
public ITable<Products> Products { get { return this.GetTable<Products>(); } } }
}
事实上这种用配置的方法在EF里面也存在,而上面方法就是通过传入参数来确定对应的连接字符串。只不过上面在配置文件就指定了默认的连接。所以我们可以不用指定默认的,用直接指定的方式来获得连接。如下
using LinqToDB;
using LinqToDB.DataProvider.SqlServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace LinqToDBExample
{
public class DbNorthwind:DataContext
{
public DbNorthwind()
: base("Aomi")
{ }
public ITable<Products> Products { get { return this.GetTable<Products>(); } } }
}
如果大家还是不满意的话,笔者也只能跪求解脱——因为笔者只知道这三种方式了。
LinqToDB的增加功能
对于EF相信大家都知道没有什么增加不增加的概念。至少笔者是这样子认为的。因为笔者没有看到对应的增加方法。很多人说难道对一个同步数据库的集合表进行增加数据就不算增加吗(这里集合表就是从IDbSet中得来的集合)。笔者认不是。EF好像只有数据有没有发生变化这个概念。你对集合表操作就表现了数据发现变化。最后提交变化的时候,EF会知道原来增加了。形式上有一点像是在用DataSet进行数据库。而LinqToDB却不是这样子,他还是有增加这个概念了。让笔者举一些列子吧。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
Products product = new Products();
product.Name = "aomi"; db.Insert(product);
} Console.ReadKey();
}
}
}
执行结果:
上面做了一个简单的操作。只是可惜LinqToDB的增加功能有一点让笔者失望。他并没有像EF那么样子。增加的时候,如果主键是自动增加(标识),那么增加成功之后会把主键同步到增加对象中。如图下,增加成之后我们看到对象ProductID还是0。
对于这一点LinqToDB到是用了一种传统的方式。扩展了一叫InsertWithIdentity方法。增加成之后返回对象主键。所以相对于上面想要把增加成之后的主建同步到对应增加对象的属性中就必须用传统的方式。笔者真的一点受不了。修改如下。
product.ProductID = Convert.ToInt32(db.InsertWithIdentity(product));
虽然上面的增加功能让笔者一时难以接受。不过作者在增加方面还扩展不少方法。让我们可以直接在对应的表属性上面操作。即是ITable接口类型的属性。这个时候只要设置对象属性值就可以了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
db.Products.Value(t => t.Name, "aomi1")
.Insert();
} Console.ReadKey();
}
}
}
看到了吧。上面由于笔者的Products类只写了一个属性。ProductID属性又是自动增加。所以只有一个Value。如果要对多个属性设置,就可以在Value在点Value。最后在点Insert或是InsertWithIdentity。
LinqToDB的更新功能
看完了LinqToDB的增加功能之后。不知道大家对LinqToDB有没有一种想要去了解冲动呢?没事,让我们看一下LinqToDB对更新方面,又做了一些什么变化。不,不能说什么变化。应该说做了什么设计。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{ Products product = new Products();
product.ProductID = ;
product.Name = "aomi update"; db.Update(product); } Console.ReadKey();
}
}
}
上面这段代码做的事情很简单——更新ProductID为79的数据的Name值。执行成功并且更新数据库。你敢信。笔者记得当场傻眼。过用EF的开发人员都知道如果我们新建一个普通的对象。注意不是从数据库里面Linq出来的。而是用new关键字新建的。这时候你意图更新但是EF却会变成增加。可是LinqToDB却不存在这样子的问题。这设计笔者给99分。1分是笔者不认同——感觉设计思想有一点乱来。早的时候笔者一直希望看到LinqToDB在操作对象上能像EF或Hibernate那样子——存在对象状态的说法。从增加的时候主键不能同步到现在新建对象更新成来看LinqToDB并没有对象持久化这个说法。也罢,必竟是一个轻量级的ORM框架。
上面的更新做法相信大家一定不会有什么烦感。如果新建的对象设置主键的值之后,都能更新。那么从数据库Linq出来的数据更不用讲了。主要是要看看一下LinqToDB给我们带来另一个更新的做法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
db.Products
.Where(t => t.ProductID == )
.Set(t => t.Name, "Update aomi")
.Update();
} Console.ReadKey();
}
}
}
由于增加功能里面的Value静态扩展方法出现,导至笔者对更新功能的Set静态扩展方法出现一点也不奇怪。如果要更新多个属性的值,只要是点多个Set就可以了。当然最后不要忘记了点Update。更新功能的扩展方法笔者还是比较喜欢的。相对于EF来讲,EF必须先到数据库获得要更新的对象。然后在修改对象的属性值才能更新。而对于LinqToDB来讲,只要一步到位。
LinqToDB的删除功能
看完了增加和更新,显然不能忘了看一下LinqToDB在删除方面是如何做的。正如上面所讲的LinqToDB并没有对象状态这一个说法。所以下面的代码删除成功了也不会觉得奇怪。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
Products product = new Products();
product.ProductID = ; db.Delete(product);
} Console.ReadKey();
}
}
}
另一种做法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
db.Products.Where(t => t.ProductID == ).Delete();
} Console.ReadKey();
}
}
}
上面的俩种做法。笔者很淡定的飘过。不过到是说明了一个问题——增删改一般都有俩种方式。一种是从IDataContext接口上扩展出来的方法。一种是从IQueryable接口或是ITable接口扩展出来的方法。
在开发一个大型项目的时候,对于多表操作显得家常便饭。所以每一个框架都有自己处理事务的功能。同样子LinqToDB也实现了事务功能。不过他的事务让笔者很舒服,不是因为他有多么好。而是他更加接近原生态。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB;
using LinqToDB.Data; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
using (DataContextTransaction transaction = db.BeginTransaction())
{
try
{
int deleteID = Convert.ToInt32(db.Products.Value(t => t.Name, "aomi79").InsertWithIdentity());
db.Products.Where(t => t.ProductID == ).Delete();
}
catch (Exception ex)
{
}
} } Console.ReadKey();
}
}
}
上面的做法用的是自动提交事务,如果你想要自己提交的事务的话,一定要设置他的参数。默认事务是自动提交事务的。如下,笔者关闭掉自动提交事务。变成手动提交了。所以传入false。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqToDB;
using LinqToDB.Data; namespace LinqToDBExample
{
class Program
{
static void Main(string[] args)
{
using(var db = new DbNorthwind())
{
using (DataContextTransaction transaction = db.BeginTransaction(false))
{
db.Products.Value(t => t.Name, "aomi83").Insert();
int deleteID = Convert.ToInt32(db.Products.Value(t => t.Name, "aomi79").InsertWithIdentity());
int count = db.Products.Where(t => t.ProductID == deleteID).Delete(); if (count <= )
{
transaction.RollbackTransaction();
}
else
{
transaction.CommitTransaction();
}
} } Console.ReadKey();
}
}
}
结束语
LinqToDB框架有他自己的优点和特色。同样子也有他的不足。而笔者想为大家介绍LinqToDB的使用的同时更想为介绍大家他是什么实现的。如果本系列中有出现错误的说法,希望大家能谅解。可以的话,请提出来让笔者也学习一下。谢谢。
LinqToDB 源码分析——前言的更多相关文章
- LinqToDB 源码分析——生成表达式树
当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...
- Netty源码分析(前言, 概述及目录)
Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...
- Struts2 源码分析——前言
笔者简言 笔者在博园里面注册是在二年前.可是那个时候我不知道要写些什么,也怕写出来被别人骂误人子弟.而现在却动笔了是因为前一段时间内我去参加一些大公司的面试,让笔者内心深处留下很多问号.最近三年来我一 ...
- LinqToDB 源码分析——生成与执行SQL语句
生成SQL语句的功能可以算是LinqToDB框架的最后一步.从上一章中我们可以知道处理完表达式树之后,相关生成SQL信息会被保存在一个叫SelectQuery类的实例.有了这个实例我们就可以生成对应的 ...
- libuv源码分析前言
Libevent,libev,libuv三者的区别所在? libevent提供了全套解决方案(事件库,非阻塞IO库,http库,DNS客户端),然而libevent使用全局变量,导致非线程安全.它的w ...
- LinqToDB 源码分析——DataContext类
LinqToDB框架是一个轻量级的ORM框架.当然,功能上来讲一定比不上Entity Framework的强大.但是在使用上总让笔者感觉有一点Entity Framework的影子.笔者想过可能的原因 ...
- LinqToDB 源码分析——设计原理
我们知道实现了IQueryable<T>接口和IQueryProvider接口就可以使用Linq To SQL的功能.关于如何去实现的话,上一章也为我们引导了一个方向.LinqToDB框架 ...
- Java容器类源码分析前言之集合框架结构(基于JDK8)
一.基本概念 Java容器类库的用途是"保存对象",容器库类分为两个不同的分支. 1.Collection.可以保存一个或多个对象,将其保存为一个序列.Collection又可以细 ...
- LinqToDB 源码分析——处理表达式树
处理表达式树可以说是所有要实现Linq To SQL的重点,同时他也是难点.笔者看完作者在LinqToDB框架里面对于这一部分的设计之后,心里有一点不知所然.由于很多代码没有文字注解.所以笔者只能接合 ...
随机推荐
- C# 利用性能计数器监控网络状态
本例是利用C#中的性能计数器(PerformanceCounter)监控网络的状态.并能够直观的展现出来 涉及到的知识点: PerformanceCounter,表示 Windows NT 性能计数器 ...
- iOS可视化动态绘制八种排序过程
前面几篇博客都是关于排序的,在之前陆陆续续发布的博客中,我们先后介绍了冒泡排序.选择排序.插入排序.希尔排序.堆排序.归并排序以及快速排序.俗话说的好,做事儿要善始善终,本篇博客就算是对之前那几篇博客 ...
- Node.js:理解stream
Stream在node.js中是一个抽象的接口,基于EventEmitter,也是一种Buffer的高级封装,用来处理流数据.流模块便是提供各种API让我们可以很简单的使用Stream. 流分为四种类 ...
- C#中将DataTable导出为HTML的方法
今天我要向大家分享一种将DataTable导出为到HTML格式的方法.有时我们需要HTML格式的输出数据, 以下代码就可以帮助我们达到目的,. 首先,我们要绑定DataTable和 DataGridV ...
- .NET跨平台之运行与Linux上的Jexus服务器
谈及.NET跨平台,已经不是什么稀奇的事儿.今天我们就以Jexus服务器的部署为例.简单示范下.在这里,我用VMWare虚拟机来搭建Linux运行环境. Linux,我们选择CentOS7.大家可以前 ...
- Javascript 代理模式模拟一个文件同步功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JDK动态代理
一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...
- bzoj1079--记忆化搜索
题目大意:有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块.所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得 ...
- continue break 区别
在循环中有两种循环方式 continue , break continue 只是跳出本次循环, 不在继续往下走, 还是开始下一次循环 break 将会跳出整个循环, 此循环将会被终止 count = ...
- Linux命令【第三篇】
执行下面命令时发现提示需要输入密码,请问提示输入的密码是哪个用户的密码. [test@oldboy ~]$ sudo su - oldboy 解答: 输入当前执行命令test账户的密码. 相关说明: ...