linq技术研究

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linq技术研究
一、Linq简介
LINQ,语言级集成查询(Language Integrated Query)
LINQ 提供了一条更常规的途径即给.Net Framework 添加一些可以应用于所有信息源( all sources of information )的具有多种用途( general-purpose )的语法查询特性( query facilities ),这是比向开发语言和运行时( runtime )添加一些关系数据( relational )特性或者类似XML 特性( XML-specific )更好的方式。

LINQ是微软将在C# 3.0中将推出的语言集成查询技术,许多人也将LINQ叫做微软的ORM。

LINQ不仅仅针对关系数据库,它只是解决一个问题Data!=Object,也就是说他解决的就是Data=Object.。

作为底层框架,可以为ORM实现提供更强大的基础平台。

LINQ to SQL集成到应用程序中需考虑的一些问题:
1、结果集的返回是IQueryable还是List,还是应该扩展为T,。

2、需要一个分页功能; LINQ to SQL本身已经有API提供了分页功能了,不过只有排序或包含标识列的查询中支持Skip方法,看看下面的分页API, 多么简单: return q.Skip((currentPage - 1) * pageSize).Take< Order >(pageSize)
3、需要一个动态排序功能, 这里List的局限性出来了, 传统的做法可能需要用一个dynamic参数来传递需要排序的列然后到SP当中来执行, 但我们已经不打算使用SP了, 也没有动态sql语句, 所有的东西都是强类型的, 然后有LINQ to SQL在运行时来帮我们转换为T-SQL语句。

首先List的话, 我们不知道到底哪个字段要排序, 如果使用字符串作为参数的话, 例如放一个string sortBy作为方法的参数, 那么在方法体内就需要做if…else或者switch的判断, 而且还要考虑倒序还是正序的排序要求, 而且你还要hard code,很明显麻烦来了.然而如果使用IQueryable却可以很好的解决所有的这些问题. 但是IQueryable不能跨assembly, 一旦跨了assembly的话, 你无法使用var来引用匿名类里面的property, 绑定到control是没有问题的, 但是客户端的动态查询却成了问题, 因为你根本不知道匿名类是什么. 那么选择IQueryable, 我们选择返回IQueryable给客户端, 分页/排
序都没有任何问题.
二、Linq语法本质
简单一点的说,Linq的本质=语法糖(自动属性)+语法糖(初始化器)+语法糖(具有隐式类型的局部变量)+语法糖(匿名类型)+语法糖(扩展方法)+语法糖(更强的类型自动推断)+语法糖(Lambda表达式)+语法糖(编译为Lambda表达式树)+类库(Lambda表达式树)+类库(Linq to Sql)+类库(Linq to Object)+类库(Linq to Xml)+类库(Linq to 其他扩展),看一下下面的实例:
1、自动属性
class Class
{
//C#2.0 属性
//private int _id;
//public int ID
//{
// get {
// return _id;
// }
// set
// {
// _id = value;
// }
//}
//C#3.0 属性可以给get set加访问修饰符
public int ID { get; private set; }
public string Name { get; set; }
public Class(int id)
{
//加了private之后的属性只能在类的内部访问
this.ID = id;
}
}
本质:和原来的属性没啥两样,简化了语法而已。

2、初始化器
private static void Initializer()
{
//C#2.0 对象初始化
//Class c = new Class(1);
// = "终极一班";
//C#3.0 对象初始化器
Class c = new Class(1) { Name = "终极一班" };
//C#2.0 集合初始化
//ClassCollection list = new ClassCollection();
//list.Add(c);
//C#3.0 集合初始化器
ClassCollection list = new ClassCollection
{
new Class(1) { Name="终极一班"},
new Class(2){Name="终极二班"}
};
foreach (Class item in list)
{
Console.WriteLine(item.ID + " " + );
}
}
本质:和原来的构造函数初始化或构造后通过属性初始化没啥两样,简化了语法而已。

对Linq的意义:和匿名类型结合起来构造查询结果集合里面的新元素类型。

3、具有隐式类型的局部变量
private static void Var()
{
// 编译过后的结果实际是int i=1; var并不是动态变量,它的类型实际上是c#编译器通过上下文推断是int
var i = 1;
var d = DateTime.Now;//=后面支持各种类型
var a = new int[] { 1, 2, 3 };//var也支持数组
foreach (var item in a)//item的类型通过C#编译器推断得知是int
{
Console.WriteLine(i);
}
//var x; // 错误,没有用来推断类型的初始化器
//var y = { 1, 2, 3 }; // 错误,不允许使用集合初始化器
//var z = null; // 错误,不允许出现空类型
}
本质:var并非动态类型,C#仍然是静态语言,引入var方便我们写代码了,可以不管“=”后面的赋值表达式类型了,由编译器自己去推断生成对应类型了。

对Linq的意义:可以自动推断出Linq查询返回的集合类型。

4、匿名类型
private static void AnonymousType()
{
/ /无须显示声明一个类,而且在初始化器里面可以获取上下文的变量——闭包
var v = new { Name = "张三", Sex = true };
Console.WriteLine();
}
本质:有了匿名类型后我们不需要显示的声明一个类型了,这个类型由C#编译器自动生成,而且利用了初始化器和var的新特性.
对Linq的意义:和初始化器结合起来构造查询结果集合里面的新元素类型。

5、扩展方法
比如我们现在想给int类型增加(扩展)一个方法,判断一个整数自身是否偶数,我们期望的语法是这样的:
private static void ExtendMethod()
{
int i = 2;
Console.WriteLine(i.IsEven());
}
注意原来int原来是没有IsEven()这个方法的,要实现这个方法,必须写一个静态类和一个静态方法。

static class MyExtention
{
//this 表示针对int的实例和索引器的this的含义是一样的,int表示给int这种类型进行扩展public static bool IsEven(this int num)
{
return num % 2 == 0;
}
}
本质:编译i.IsEven()的本质是C#编译器生成了了MyExtention.IsEven(i)的代码,实际上仍然没有破坏类型的结构,并不是真的象语法那样平白无故给int增加了一个IsEven()方法,和设计模式里面的Visitor模式动态注入方法还是有区别的。

对Linq的意义:用来对集合类型扩展不同的查询方法。

6、Lambda表达式和Linq查询
接下来我们通过一个例子来看一下Lambda表达式和Linq查询的关系:我们现在想给ClassCollection增加一个过滤方法,方法的目的是能够过滤返回班级名称为“终极一班”的集合来。

这也就是我们所说的Lambda表达式了:
private static void LambdaLinq()
{
var classes = GetClasses();
string className = "终极一班";
// C#3.0 Lambda表达式
var result = classes.Filter(c=> == className);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + );
}
}
“=> ”左边的就是我们上面匿名方法的参数列表,右边的是方法里,实际上lambda表达式也可以写成如下形式:
Class c => == className
(Class c) => == className
(Class c) => {return == className;}
(x,y)=> x+y;//多参数
等等,函数的返回类型也是由编译器根据"=> "右边的表达式自动推断出来的。

而且需要提到的是由于Filter是扩展方法的缘故,而且Filter方法返回类型是ClassCollection,所以可以无限扩展下去,例如
var result = classes.Filter( c => == className).Filter(c=> c.ID> 1);
这就是扩展方法的魅力所在!
实际上不知不觉,我们已经实现了Linq里面的一个Where功能了
我们现在导入命名空间
using System.Linq;
然后会发现classes这个实例会增加了很多扩展方法例如Where,OrderBy,这些方法实际上就是一些给实现了IEnumerable接口的类型的扩展方法,说白了就是针对集合类型的一些相关方法,比如过滤、排序、合并、分组等方法,这些方法的返回类型依然是IEnumerable,当然这些方法都离不开我们的Lambda表达式做参数。

private static void LambdaLinq()
{
var classes = GetClasses();
string className = "终极一班";
// C#3.0里面的Where扩展方法(专门给实现了IEnumerable接口的类做扩展)
var result = classes.Where(c => == className);
foreach (var item in result)
{
Console.WriteLine(item.ID+ " " + );
}
}
我们还可以这样可以无限扩展下去:var result = classes.Where(c => ==
className).OrderBy(c=> c.ID);
这样写针对IEnumarable类型的查询其实已经不错了,微软觉得还不过瘾,又提供了我们传说中的Linq查询表达式.
private static void LambdaLinq()
{
var classes = GetClasses();
string className = "终极一班";
// Linq查询表达式
var result = from c in classes where ==className orderby c.ID select c;
foreach (var item in result)
{
Console.WriteLine(item.ID+" "+);
}
}
其实它的本质都是面向对象的一些东西,并没有创造出一些什么新的东西来,这样我们才可以真正理解语言。

最后一个稍微复杂一些的Linq查询,就是班级和学生结合的一个连接,连接的条件是班级的id和学生的所属班级id,然后生成一个新的集合,这个集合里面的元素成员包括班级名称和学生名称。

涉及到的相关类如下:
class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int ClassID { get; set; }
}
class StudentCollection : List <Student>
{
}
获取学生集合的工厂方法:
static StudentCollection GetStudents()
{
return new StudentCollection()
{
new Student { ID=1,Name="大东", ClassID=1},
new Student{ID=2,Name="亚瑟",ClassID=1},
new Student { ID=3,Name="小雨", ClassID=1},
new Student{ID=4,Name="雷克斯",ClassID=1},
new Student{ID=2,Name="张三",ClassID=2},
new Student { ID=3,Name="李四", ClassID=2},
new Student{ID=4,Name="王二麻子",ClassID=2}
};
}
private static void LambdaLinq()
{
var classes = GetClasses();
var students=GetStudents();
// Linq查询表达式Join
var result = from c in classes
join s in students on c.ID equals s.ClassID
select new { ClassName = , StudentName = };//匿名类型和初始化器新特性的使用
//var result = classes.Join(students,c=> c.ID,s=> s.ClassID,(c,s)=>
new {ClassName=,StudentName=}
foreach (var item in result)
{
//注意元素的属性成员已经改变
Console.WriteLine(item.ClassName + " " + item.StudentName);
}
}
三、Linq To Sql集成数据库语言的优劣
1、Linq To Sql的优点
在Linq To Sql推出之前,我们只是把sql语句形成一个string,然后,通过 传给sql server,返回结果集.这里的缺陷就是,如果你sql语句写的有问题,只有到运行时才知道.而且并不所有的人都懂数据库的。

Linq To SQL在一切围绕数据的项目内都可以使用。

特别是在项目中缺少SQL Server方面的专家时,Linq To SQl的强大的功能可以帮我们快速的完成项目。

Linq To SQL的推出,是让大家从烦琐的技术细节中解脱出来,更加关注项目的逻辑。

Linq To Sql的出现,大大降低了数据库应用程序开发的门槛,它实质是事先为你构架了数据访问层,势必
将加快数据库应用程序的开发进度。

Linq To Sql解放了众多程序员,让他们的把更多的精力放到业务逻辑以及code上,而不是数据库。

对于初学者来讲,Linq To Sql可以让他们迅速进入数据库应用程序开发领域,节约了培训成本。

Linq To SQl 的实现,是在和C#2.0的基础上的。

它通过自动翻译sql 语句,并把结果集创建成对象并返回。

这里我们可以看出,发送到SQL Server 端的sql语句是Linq To Sql自动生成的。

这对不懂sql的人来说,无疑是个福音。

第二,Linq To Sql语句是在编译期间就做检查的。

而不是运行时检查。

这样,那里出了问题,可以及时更改,而不是到了运行时才发现问题。

第三,Linq To Sql 是针对对象操作的,更符合今天的oo呼声。

在Linq To SQL之前,在Java领域有Hibernate,在net领域有NHibernate技术,来实现object/relational 持久和查询服务。

那和NHibernate比起来,它又有那些优势呢.第一,影射代码自动生成。

VS2008提供了SqlMetal和OR Designer两个工具来完成此步骤。

而在NHibernate中,你不得不自己手工写。

第二,影射代码有更多的选择.NHibernate 只能把数据库的信息配置在一个xml中,而Linq To Sql有两种方式,一个是放到XML中,我们称为Externl Mapping, 再一种就是以Attribute的形式,存在于各个property中。

2、Linq To Sql的缺点
Linq To Sql在动态构造语句时,比拼接sql麻烦很多。

在Linq To Sql进阶系列动态查询中推荐使用object的查询。

这符合Linq To Sql的设计原则。

因为,它主要是为了解决data!=objects 的问题而产生的.它所有的操作均针对object,那就让我们使用object的查询吧。

当然,依然会有人习惯拼接字符串.我并不认为这是个坏毛病。

只是有点不符合oo思想而已。

事实上,在Linq To Sql中,你依然可以使用拼接字符串的形式,而不使用它提供的Query Expression. 它提供了这么两个接口,一个是,db.ExecuteQuery(string sql); 另一个是,
db.ExecuteCommand(string sql);这两个函数都是直接接收sql语句的.习惯拼接的人,依然可以调用它们来实现你的程序。

特别是第一个,其返回的就是一个对象的集合,多少还是有点oo思想的。

看下面的例子:
var products = db.ExecuteQuery(
"SELECT [Product List].ProductID, [Product List].ProductName " +
"FROM Products AS [Product List] " +
"WHERE [Product List].Discontinued = 0 " +
"ORDER BY [Product List].ProductName;"
).ToList();
它返回的就是product的集合。

而不是什么dataset和datatable之类的。

这里,你可以大胆的使用该函数继续拼接你的sql吧,再看下面这个:
db.ExecuteCommand("UPDATE Products SET UnitPrice = UnitPrice + 1.00");
它在做批处理的时候,你想不用它,都不行.当然,你如果觉得性能不是问题的话,那就用submitchange方法来做更新好了。

简单明了的说,Linq To Sql在批处理更新的时候,SubmitChange只会一个个的更新。

浪费时间资源.而这个接口,恰好满足了批处理更新或删除的问题。

从这两个例子,我们可以看出。

没有任何方案是万能的。

各有各的优点。

3、Linq To Sql的性能
Linq 的性能已经被好多人提及.它确实比自己实现的查找要慢.但是当数据量
特别大时,更多是时间是花在分配虚拟内存上了,那么他们的差别就不是那么明显了。

Linq To Sql是又如何提升性能的?第一,采用延迟加载(deferred loading)技术。

语句是声明了,但是并不立即执行,而是在真正需要的时候才执行。

第二,采用缓存技术。

已经取到内存的数据,再依次提取时,会先从缓存中返回,而不是再次访问数据库。

相关文档
最新文档