20.2 LINQ与Web应用程序
在ASP.NET应用程序开发中,常常需要涉及到数据的显式和整合,使用ASP.NET 2.0中提供的控件能够编写用户控件,开发人员还能够选择开发自定义控件进行数据显示和整合,但是在数据显示和整合过程中,开发人员往往需要大量的连接、关闭连接等操作,而且传统的方法也破坏了面向对象的特性,使用LINQ能够方便的使用面向对象的方法进行数据库操作。
20.2.1 创建使用LINQ的Web应用程序
创建LINQ的Web应用程序非常的容易,只要创建Web应用程序时选择的平台是基于.NET Framework 3.5的就能够创建使用LINQ的Web应用程序,如图20-4所示。
图20-4 选择.NET Framework 3.5
当创建一个基于系统.NET Framework 3.5的应用程序,系统就能够自动为应用程序创建LINQ所需要的命名空间,示例代码如下所示。
上述命名空间提供了应用程序中使用LINQ所需要的基础类和枚举,在ASP.NET应用程序中就能够使用LINQ查询语句进行查询,示例代码如下所示。
上述代码在ASP.NET页面中执行了一段LINQ查询,查询字符串中包含“C#”的字符串,运行后如图20-5所示。
图20-5 ASP.NET执行LINQ查询
在ASP.NET中能够使用LINQ进行数据集的查询,Visual Studio 2008已经将LINQ整合成为编程语言中的一部分,基于.NET Framework 3.5的应用程序都可以使用LINQ特性进行数据访问和整合。
20.2.2 基本的LINQ数据查询
使用LINQ能够对数据集进行查询,在ASP.NET中,可以创建一个新的LINQ数据库进行数据集查询,右击现有项目,单击【添加新项】选项,选择【LINQ to SQL类】选项,如图20-6所示。
图20-6 创建LINQ to SQL类
创建一个LINQ to SQL类,能够映射一个数据库,实现数据对象的创建,如图20-7所示。创建一个LINQ to SQL类后,可以直接在服务资源管理器中拖动相应的表到LINQ to SQL类文件中,如图20-8所示。
图20-7 服务资源管理器
图20-8 拖动一个表
开发人员能够直接将服务资源管理器中的表拖动到LINQ to SQL类中,在LINQ to SQL类文件中就会呈现一个表的视图。在视图中,开发人员能够在视图中添加属性和关联,并且能够在LINQ to SQL类文件中可以设置多个表,进行可视化关联操作。
创建一个LINQ to SQL类文件后,LINQ to SQL类就将数据进行对象化,这里的对象化就是以面向对象的思想针对一个数据集建立一个相应的类,开发人员能够使用LINQ to SQL创建的类进行数据库查询和整合操作,示例代码如下所示。
上述创建了一个MyData.dbml的LINQ to SQL文件,开发人员能够直接使用该类的对象提供数据操作。上述代码使用了LINQ to SQL文件提供的类进行数据查询,LINQ查询语句示例代码如下所示。
上述代码使用了LINQ查询语句查询了一个mynews表中ID为1的行,使用LINQ to SQL文件提供的对象能够快速的进行数据集中对象的操作。创建一个MyData.dbml的LINQ to SQL文件,其中MyDataDataContext为类的名称,该类提供LINQ to SQL操作方法,示例代码如下所示。
上述代码使用了LINQ to SQL文件提供的类创建了一个对象data,data对象包含数据中表的集合,通过“.”操作符可以选择相应的表,示例代码如下所示。
使用LINQ查询后运行结果如图20-9所示。
图20-9 LINQ执行数据库查询
使用LINQ技术能够方便的进行数据库查询和整合操作,LINQ不仅能够实现类似SQL语句的查询操作,还能够支持.NET编程方法进行数据查询条件语句的编写。使用LINQ技术进行数据查询的顺序如下所示:
1)创建LINQ to SQL文件:创建一个LINQ to SQL类文件进行数据集封装。
2)动数据表:将数据表拖动到LINQ to SQL类文件中,可以进行数据表的可视化操作。
3)使用LINQ to SQL类文件:使用LINQ to SQL类文件提供的数据集的封装进行数据操作。
使用LINQ to SQL类文件能够极快的创建一个LINQ到SQL数据库的映射并进行数据集对象的封装,开发人员能够使用面向对象的方法进行数据集操作并提供快速开发的解决方案。
20.2.3 IEnumerable和IEnumerable<T>接口
IEnumerable和IEnumerable<T>接口在.NET中是非常重要的接口,它允许开发人员定义foreach语句功能的实现并支持非泛型方法的简单的迭代,IEnumerable和IEnumerable<T>接口是.NET Framework中最基本的集合访问器,这两个接口对于LINQ的理解是非常重要的。
在面向对象的开发过程中,常常需要创建若干对象,并进行对象的操作和查询,在创建对象前,首先需要声明一个类为对象提供描述,示例代码如下所示。
上述代码定义了一个Person类并抽象一个Person类的属性,这些属性包括Name和Age。Name和Age属性分别用于描述Person的名字和年龄,用于数据初始化。初始化之后的数据就需要创建一系列Person对象,通过这些对象的相应属性能够进行对象的访问和遍历,示例代码如下所示。
上述代码创建并初始化了2个Person对象,并通过foreach语法进行对象的遍历。但是上述代码是在数组中进行查询的,就是说如果要创建多个对象,则必须创建一个对象的数组,如上述代码中的Per变量,而如果需要直接对对象的集合进行查询,却不能够实现查询功能。例如增加一个构造函数,该构造函数用户构造一组Person对象,示例代码如下所示。
上述构造函数动态的构造了一组People类的对象,那么应该也能够使用foreach语句进行遍历,示例代码如下所示。
在上述代码的foreach语句中,直接在Person类的集合中进行查询,系统则会报错“ConsoleApplication1.Person”不包含“GetEnumerator”的公共定义,因此foreach语句不能作用于“ConsoleApplication1.Person”类型的变量,因为Person类并不支持foreach语句进行遍历。为了让相应的类能够支持foreach语句执行遍历操作,则需要实现派生自类IEnumerable并实现IEnumerable接口,示例代码如下所示。
为了让自定义类型能够支持foreach语句,则必须对Person类的构造函数进行编写并实现接口,示例代码如下所示。
上述代码重构了Person类并实现了接口,接口实现的具体方法如下所示。
上述代码实现了foreach语句的功能,当开发Person类初始化后就可以直接使用Personal类对象的集合进行LINQ查询,示例代码如下所示。
从上述代码中可以看出,初始化Person对象时初始化的是一个对象的集合,在该对象的集合中可以通过LINQ直接进行对象的操作,这样做即封装了Person对象也能够让编码更加易读。在.NET Framework 3.5中,LINQ支持数组的查询,开发人员不必自己手动创建IEnumerable和IEnumerable<T>接口以支持某个类型的foreach编程方法,但是IEnumerable和IEnumerable<T>是LINQ中非常重要的接口,在LINQ中也大量的使用IEnumerable和IEnumerable<T>进行封装,示例代码如下所示。
上述代码为LINQ内部的封装,从代码中可以看到,在LINQ内部也大量的使用了IEnumerable和IEnumerable<T>接口实现LINQ查询。IEnumerable原本就是.NET Framework中最基本的集合访问器,而LINQ是面向关系(有序N元组集合)的,自然也就是面向IEnumerable<T>的,所以了解IEnumerable和IEnumerable<T>对LINQ的理解是有一定帮助的。
20.2.4 IQueryProvider和IQueryable<T>接口
IQueryable和IQueryable<T>同样是LINQ中非常重要的接口,在LINQ查询语句中,IQueryable和IQueryable<T>接口为LINQ查询语句进行解释和翻译工作,开发人员能够通过重写IQueryable和IQueryable<T>接口以实现用不同的方法进行不同的LINQ查询语句的解释。
IQueryable<T>继承于IEnumerable<T>和IQueryable接口,在IQueryable中包括两个重要的属性,这两个属性分别为Expression和Provider。Expression和Provider分别表示获取与IQueryable 的实例关联的表达式目录树和获取与数据源关联的查询提供程序,Provider作为其查询的翻译程序实现LINQ查询语句的解释。通过IQueryable和IQueryable<T>接口,开发人员能够自定义LINQ Provider。
注意:Provider可以被看做是一个提供者,用于提供LINQ中某个语句的解释工具,在LINQ中通过编程的方法能够实现自定义Provider。
在IQueryable和IQueryable<T>接口中,还需要另外一个接口,这个接口就是IQueryProvider,该接口用于分解表达式,实现LINQ查询语句的解释工作,这个接口也是整个算法的核心。IQueryable<T>接口在MSDN中的定义如下所示。
上述代码定义了IQueryable<T>接口的规范,用于保持数据源和查询状态,IQueryProvider在MSDN中定义如下所示。
IQueryProvider用于LINQ查询语句的核心算法的实现,包括分解表达式和表达式计算等。为了能够创建自定义LINQ Provider,可以编写接口的实现。示例代码如下所示。
上述代码用于构造一个可用来执行表达式计算的IQueryable 对象,在接口中可以看到需要实现两个相同的执行表达式的IQueryable 对象,另一个则是执行表达式对象的集合,其实现代码如下所示。
而作为表达式解释和翻译的核心接口,则需要通过算法实现相应Execute方法,示例代码如下所示。
上述代码通过使用lambda表达式进行表达式的计算,实现了LINQ中查询的解释功能。在LINQ中,对于表达式的翻译和执行过程都是通过IQueryProvider和IQueryable<T>接口来实现的。IQueryProvider和IQueryable<T>实现用户表达式的翻译和解释,在LINQ应用程序中,通常无需通过IQueryProvider和IQueryable<T>实现自定义LINQ Provider,因为LINQ已经提供强大表达式查询和计算功能。了解IQueryProvider和IQueryable<T>接口有助于了解LINQ内部是如何执行的。
20.2.5 LINQ相关的命名空间
LINQ开发为开发人员提供了便利,可以让开发人员以统一的方式对IEnumerable<T>接口的对象、数据库、数据集以及XML文档进行访问。从整体上来说,LINQ是这一系列访问技术的统称,对于不同的数据库和对象都有自己的LINQ名称,例如LINQ to SQL、LINQ to Object等等。当使用LINQ操作不同的对象时,可能使用不同的命名空间。常用的命名空间如下所示。
1)System.Data.Linq: 该命名空间包含支持与 LINQ to SQL 应用程序中的关系数据库进行交互的类。
2)System.Data.Linq.Mapping:该命名空间包含用于生成表示关系数据库的结构和内容的 LINQ to SQL 对象模型的类。
3)System.Data.Linq.SqlClient:该命名空间包含与SQL Server 进行通信的提供程序类,以及包含查询帮助器方法的类。
4)System.Linq:该命名空间提供支持使用语言集成查询 (LINQ)进行查询的类和接口。
5)System.Linq.Expression:该命名空间包含一些类、接口和枚举,它们使语言级别的代码表达式能够表示为表达式树形式的对象。
6)System.Xml.Linq:包含 LINQ to XML 的类,LINQ to XML 是内存中的 XML 编程接口。
LINQ中常用的命名空间为开发人员提供LINQ到数据库和对象的简单的解决方案,开发人员能够通过这些命名空间提供的类进行数据查询和整理,这些命名空间统一了相应的对象的查询方法,如数据集和数据库都可以使用类似的LINQ语句进行查询操作。
20.2.1 创建使用LINQ的Web应用程序
创建LINQ的Web应用程序非常的容易,只要创建Web应用程序时选择的平台是基于.NET Framework 3.5的就能够创建使用LINQ的Web应用程序,如图20-4所示。
图20-4 选择.NET Framework 3.5
当创建一个基于系统.NET Framework 3.5的应用程序,系统就能够自动为应用程序创建LINQ所需要的命名空间,示例代码如下所示。
+展开
-C#
using System.Xml.Linq;//使用LINQ命名空间
using System.Linq;//使用LINQ命名空间
using System.Linq;//使用LINQ命名空间
上述命名空间提供了应用程序中使用LINQ所需要的基础类和枚举,在ASP.NET应用程序中就能够使用LINQ查询语句进行查询,示例代码如下所示。
+展开
-C#
protected void Page_Load(object sender, EventArgs e)
{
string[] str = { "我爱C#", "我喜欢C#", "我做C#开发", "基于.NET平台", "LINQ应用" };//数据集
var s = from n in str where n.Contains("C#") select n;//执行LINQ查询
foreach (var t in s)//遍历对象
{
Response.Write(t.ToString() + "<br/>");//输出查询结果
}
}
{
string[] str = { "我爱C#", "我喜欢C#", "我做C#开发", "基于.NET平台", "LINQ应用" };//数据集
var s = from n in str where n.Contains("C#") select n;//执行LINQ查询
foreach (var t in s)//遍历对象
{
Response.Write(t.ToString() + "<br/>");//输出查询结果
}
}
上述代码在ASP.NET页面中执行了一段LINQ查询,查询字符串中包含“C#”的字符串,运行后如图20-5所示。
图20-5 ASP.NET执行LINQ查询
在ASP.NET中能够使用LINQ进行数据集的查询,Visual Studio 2008已经将LINQ整合成为编程语言中的一部分,基于.NET Framework 3.5的应用程序都可以使用LINQ特性进行数据访问和整合。
20.2.2 基本的LINQ数据查询
使用LINQ能够对数据集进行查询,在ASP.NET中,可以创建一个新的LINQ数据库进行数据集查询,右击现有项目,单击【添加新项】选项,选择【LINQ to SQL类】选项,如图20-6所示。
图20-6 创建LINQ to SQL类
创建一个LINQ to SQL类,能够映射一个数据库,实现数据对象的创建,如图20-7所示。创建一个LINQ to SQL类后,可以直接在服务资源管理器中拖动相应的表到LINQ to SQL类文件中,如图20-8所示。
图20-7 服务资源管理器
图20-8 拖动一个表
开发人员能够直接将服务资源管理器中的表拖动到LINQ to SQL类中,在LINQ to SQL类文件中就会呈现一个表的视图。在视图中,开发人员能够在视图中添加属性和关联,并且能够在LINQ to SQL类文件中可以设置多个表,进行可视化关联操作。
创建一个LINQ to SQL类文件后,LINQ to SQL类就将数据进行对象化,这里的对象化就是以面向对象的思想针对一个数据集建立一个相应的类,开发人员能够使用LINQ to SQL创建的类进行数据库查询和整合操作,示例代码如下所示。
+展开
-C#
protected void Page_Load(object sender, EventArgs e)
{
MyDataDataContext data = new MyDataDataContext();//使用LINQ类
var s = from n in data.mynews where n.ID==1 select n; //执行查询
foreach (var t in s)//遍历对象
{
Response.Write(t.TITLE.ToString() + "<br/>");//输出对象
}
}
{
MyDataDataContext data = new MyDataDataContext();//使用LINQ类
var s = from n in data.mynews where n.ID==1 select n; //执行查询
foreach (var t in s)//遍历对象
{
Response.Write(t.TITLE.ToString() + "<br/>");//输出对象
}
}
上述创建了一个MyData.dbml的LINQ to SQL文件,开发人员能够直接使用该类的对象提供数据操作。上述代码使用了LINQ to SQL文件提供的类进行数据查询,LINQ查询语句示例代码如下所示。
+展开
-C#
var s = from n in data.mynews where n.ID==1 select n; //编写查询语句
上述代码使用了LINQ查询语句查询了一个mynews表中ID为1的行,使用LINQ to SQL文件提供的对象能够快速的进行数据集中对象的操作。创建一个MyData.dbml的LINQ to SQL文件,其中MyDataDataContext为类的名称,该类提供LINQ to SQL操作方法,示例代码如下所示。
+展开
-C#
MyDataDataContext data = new MyDataDataContext();//使用LINQ类
上述代码使用了LINQ to SQL文件提供的类创建了一个对象data,data对象包含数据中表的集合,通过“.”操作符可以选择相应的表,示例代码如下所示。
+展开
-C#
data.mynews//选择相应表
使用LINQ查询后运行结果如图20-9所示。
图20-9 LINQ执行数据库查询
使用LINQ技术能够方便的进行数据库查询和整合操作,LINQ不仅能够实现类似SQL语句的查询操作,还能够支持.NET编程方法进行数据查询条件语句的编写。使用LINQ技术进行数据查询的顺序如下所示:
1)创建LINQ to SQL文件:创建一个LINQ to SQL类文件进行数据集封装。
2)动数据表:将数据表拖动到LINQ to SQL类文件中,可以进行数据表的可视化操作。
3)使用LINQ to SQL类文件:使用LINQ to SQL类文件提供的数据集的封装进行数据操作。
使用LINQ to SQL类文件能够极快的创建一个LINQ到SQL数据库的映射并进行数据集对象的封装,开发人员能够使用面向对象的方法进行数据集操作并提供快速开发的解决方案。
20.2.3 IEnumerable和IEnumerable<T>接口
IEnumerable和IEnumerable<T>接口在.NET中是非常重要的接口,它允许开发人员定义foreach语句功能的实现并支持非泛型方法的简单的迭代,IEnumerable和IEnumerable<T>接口是.NET Framework中最基本的集合访问器,这两个接口对于LINQ的理解是非常重要的。
在面向对象的开发过程中,常常需要创建若干对象,并进行对象的操作和查询,在创建对象前,首先需要声明一个类为对象提供描述,示例代码如下所示。
+展开
-C#
using System;
using System.Collections.Generic;
using System.Linq;//使用LINQ命名控件
using System.Text;
namespace IEnumeratorSample
{
class Person//定义一个Person类
{
public string Name;//定义Person的名字
public string Age;//定义Person的年龄
public Person(string name, string age)//为Person初始化(构造函数)
{
Name = name;//配置Name值
Age = age;//配置Age值
}
}
using System.Collections.Generic;
using System.Linq;//使用LINQ命名控件
using System.Text;
namespace IEnumeratorSample
{
class Person//定义一个Person类
{
public string Name;//定义Person的名字
public string Age;//定义Person的年龄
public Person(string name, string age)//为Person初始化(构造函数)
{
Name = name;//配置Name值
Age = age;//配置Age值
}
}
上述代码定义了一个Person类并抽象一个Person类的属性,这些属性包括Name和Age。Name和Age属性分别用于描述Person的名字和年龄,用于数据初始化。初始化之后的数据就需要创建一系列Person对象,通过这些对象的相应属性能够进行对象的访问和遍历,示例代码如下所示。
+展开
-C#
class Program
{
static void Main(string[] args)
{
Person[] per = new Person[2]//创建并初始化2个Person对象
{
new Person("guojing","21"),//通过构造函数构造对象
new Person("muqing","21"),//通过构造函数构造对象
};
foreach (Person p in per)//遍历对象
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
Console.ReadKey();
}
}
}
{
static void Main(string[] args)
{
Person[] per = new Person[2]//创建并初始化2个Person对象
{
new Person("guojing","21"),//通过构造函数构造对象
new Person("muqing","21"),//通过构造函数构造对象
};
foreach (Person p in per)//遍历对象
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
Console.ReadKey();
}
}
}
上述代码创建并初始化了2个Person对象,并通过foreach语法进行对象的遍历。但是上述代码是在数组中进行查询的,就是说如果要创建多个对象,则必须创建一个对象的数组,如上述代码中的Per变量,而如果需要直接对对象的集合进行查询,却不能够实现查询功能。例如增加一个构造函数,该构造函数用户构造一组Person对象,示例代码如下所示。
+展开
-C#
private Person[] per;
public Person(Person[] array)//重载构造函数,迭代对象
{
per = new Person[array.Length];//创建对象
for (int i = 0; i < array.Length; i++)//遍历初始化对象
{
per[i] = array[i];//数组赋值
}
}
public Person(Person[] array)//重载构造函数,迭代对象
{
per = new Person[array.Length];//创建对象
for (int i = 0; i < array.Length; i++)//遍历初始化对象
{
per[i] = array[i];//数组赋值
}
}
上述构造函数动态的构造了一组People类的对象,那么应该也能够使用foreach语句进行遍历,示例代码如下所示。
+展开
-C#
Person personlist = new Person(per); //创建对象
foreach (Person p in personlist)//遍历对象
{
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
}
foreach (Person p in personlist)//遍历对象
{
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
}
在上述代码的foreach语句中,直接在Person类的集合中进行查询,系统则会报错“ConsoleApplication1.Person”不包含“GetEnumerator”的公共定义,因此foreach语句不能作用于“ConsoleApplication1.Person”类型的变量,因为Person类并不支持foreach语句进行遍历。为了让相应的类能够支持foreach语句执行遍历操作,则需要实现派生自类IEnumerable并实现IEnumerable接口,示例代码如下所示。
+展开
-C#
public IEnumerator GetEnumerator()//实现接口
{
return new GetEnum(_people);
}
{
return new GetEnum(_people);
}
为了让自定义类型能够支持foreach语句,则必须对Person类的构造函数进行编写并实现接口,示例代码如下所示。
+展开
-C#
class Person:IEnumerable//派生自IEnumerable,同样定义一个Personl类
{
public string Name;//创建字段
public string Age;//创建字段
public Person(string name, string age)//字段初始化
{
Name = name;//配置Name值
Age = age;//配置Age值
}
public IEnumerator GetEnumerator()//实现接口
{
return new PersonEnum(per);//返回方法
}
}
{
public string Name;//创建字段
public string Age;//创建字段
public Person(string name, string age)//字段初始化
{
Name = name;//配置Name值
Age = age;//配置Age值
}
public IEnumerator GetEnumerator()//实现接口
{
return new PersonEnum(per);//返回方法
}
}
上述代码重构了Person类并实现了接口,接口实现的具体方法如下所示。
+展开
-C#
class PersonEnum : IEnumerator//实现foreach语句内部,并派生
{
public Person[] _per;//实现数组
int position = -1;//设置“指针”
public PersonEnum(Person[] list)
{
_per = list;//实现list
}
public bool MoveNext()//实现向前移动
{
position++;//位置增加
return (position < _per.Length); //返回布尔值
}
public void Reset() //位置重置
{
position = -1;//重置指针为-1
public object Current//实现接口方法
{
get
{
try
{
return _per[position];//返回对象
}
catch (IndexOutOfRangeException)//捕获异常
{
throw new InvalidOperationException();//抛出异常信息
}
}
}
}
{
public Person[] _per;//实现数组
int position = -1;//设置“指针”
public PersonEnum(Person[] list)
{
_per = list;//实现list
}
public bool MoveNext()//实现向前移动
{
position++;//位置增加
return (position < _per.Length); //返回布尔值
}
public void Reset() //位置重置
{
position = -1;//重置指针为-1
public object Current//实现接口方法
{
get
{
try
{
return _per[position];//返回对象
}
catch (IndexOutOfRangeException)//捕获异常
{
throw new InvalidOperationException();//抛出异常信息
}
}
}
}
上述代码实现了foreach语句的功能,当开发Person类初始化后就可以直接使用Personal类对象的集合进行LINQ查询,示例代码如下所示。
+展开
-C#
static void Main(string[] args)
{
Person[] per = new Person[2]//同样初始化并定义2个Person对象
{
new Person("guojing","21"),//构造创建新的对象
new Person("muqing","21"),//构造创建新的对象
};
Person personlist = new Person(per);//初始化对象集合
foreach (Person p in personlist)//使用foreach语句
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
Console.ReadKey();
}
{
Person[] per = new Person[2]//同样初始化并定义2个Person对象
{
new Person("guojing","21"),//构造创建新的对象
new Person("muqing","21"),//构造创建新的对象
};
Person personlist = new Person(per);//初始化对象集合
foreach (Person p in personlist)//使用foreach语句
Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age);
Console.ReadKey();
}
从上述代码中可以看出,初始化Person对象时初始化的是一个对象的集合,在该对象的集合中可以通过LINQ直接进行对象的操作,这样做即封装了Person对象也能够让编码更加易读。在.NET Framework 3.5中,LINQ支持数组的查询,开发人员不必自己手动创建IEnumerable和IEnumerable<T>接口以支持某个类型的foreach编程方法,但是IEnumerable和IEnumerable<T>是LINQ中非常重要的接口,在LINQ中也大量的使用IEnumerable和IEnumerable<T>进行封装,示例代码如下所示。
+展开
-C#
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source,Func<TSource, Boolean> predicate)//内部实现
{
foreach (TSource element in source)//内部遍历传递的集合
{
if (predicate(element))
yield return element;//返回集合信息
}
}
(this IEnumerable<TSource> source,Func<TSource, Boolean> predicate)//内部实现
{
foreach (TSource element in source)//内部遍历传递的集合
{
if (predicate(element))
yield return element;//返回集合信息
}
}
上述代码为LINQ内部的封装,从代码中可以看到,在LINQ内部也大量的使用了IEnumerable和IEnumerable<T>接口实现LINQ查询。IEnumerable原本就是.NET Framework中最基本的集合访问器,而LINQ是面向关系(有序N元组集合)的,自然也就是面向IEnumerable<T>的,所以了解IEnumerable和IEnumerable<T>对LINQ的理解是有一定帮助的。
20.2.4 IQueryProvider和IQueryable<T>接口
IQueryable和IQueryable<T>同样是LINQ中非常重要的接口,在LINQ查询语句中,IQueryable和IQueryable<T>接口为LINQ查询语句进行解释和翻译工作,开发人员能够通过重写IQueryable和IQueryable<T>接口以实现用不同的方法进行不同的LINQ查询语句的解释。
IQueryable<T>继承于IEnumerable<T>和IQueryable接口,在IQueryable中包括两个重要的属性,这两个属性分别为Expression和Provider。Expression和Provider分别表示获取与IQueryable 的实例关联的表达式目录树和获取与数据源关联的查询提供程序,Provider作为其查询的翻译程序实现LINQ查询语句的解释。通过IQueryable和IQueryable<T>接口,开发人员能够自定义LINQ Provider。
注意:Provider可以被看做是一个提供者,用于提供LINQ中某个语句的解释工具,在LINQ中通过编程的方法能够实现自定义Provider。
在IQueryable和IQueryable<T>接口中,还需要另外一个接口,这个接口就是IQueryProvider,该接口用于分解表达式,实现LINQ查询语句的解释工作,这个接口也是整个算法的核心。IQueryable<T>接口在MSDN中的定义如下所示。
+展开
-C#
public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable
{
}
public interface IQueryable : IEnumerable
{
Type ElementType { get; }//获取元素类型
Expression Expression { get; } //获取表达式
IQueryProvider Provider { get; }//获取提供者
}
{
}
public interface IQueryable : IEnumerable
{
Type ElementType { get; }//获取元素类型
Expression Expression { get; } //获取表达式
IQueryProvider Provider { get; }//获取提供者
}
上述代码定义了IQueryable<T>接口的规范,用于保持数据源和查询状态,IQueryProvider在MSDN中定义如下所示。
+展开
-C#
public interface IQueryProvider
{
IQueryable CreateQuery(Expression expression);//创建可执行对象
IQueryable<TElement> CreateQuery<TElement>(Expression expression);//创建可执行对象
object Execute(Expression expression); //计算表达式
TResult Execute<TResult>(Expression expression);//计算表达式
}
{
IQueryable CreateQuery(Expression expression);//创建可执行对象
IQueryable<TElement> CreateQuery<TElement>(Expression expression);//创建可执行对象
object Execute(Expression expression); //计算表达式
TResult Execute<TResult>(Expression expression);//计算表达式
}
IQueryProvider用于LINQ查询语句的核心算法的实现,包括分解表达式和表达式计算等。为了能够创建自定义LINQ Provider,可以编写接口的实现。示例代码如下所示。
+展开
-C#
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
query.expression = expression;//声明表达式
return (IQueryable<TElement>)query;//返回query对象
}
{
query.expression = expression;//声明表达式
return (IQueryable<TElement>)query;//返回query对象
}
上述代码用于构造一个可用来执行表达式计算的IQueryable 对象,在接口中可以看到需要实现两个相同的执行表达式的IQueryable 对象,另一个则是执行表达式对象的集合,其实现代码如下所示。
+展开
-C#
public IQueryable CreateQuery(Expression expression)
{
return CreateQuery<T>(expression);//返回表达式的集合
}
{
return CreateQuery<T>(expression);//返回表达式的集合
}
而作为表达式解释和翻译的核心接口,则需要通过算法实现相应Execute方法,示例代码如下所示。
+展开
-C#
public TResult Execute<TResult>(Expression expression)
{
var exp = expression as MethodCallExpression;//创建表达式对象
var data = ((exp.Arguments[0] as ConstantExpression).Value as MyQuery<T>).Data;
var func = (exp.Arguments[1] as UnaryExpression).Operand as Expression
<System.Func<T, bool>>;
var lambda = Expression.Lambda<Func<T, bool>>(func.Body, func.Parameters[0]);
var r = data.Where(lambda.Compile());//编译表达式
return (TResult)r.GetEnumerator();
}
{
var exp = expression as MethodCallExpression;//创建表达式对象
var data = ((exp.Arguments[0] as ConstantExpression).Value as MyQuery<T>).Data;
var func = (exp.Arguments[1] as UnaryExpression).Operand as Expression
<System.Func<T, bool>>;
var lambda = Expression.Lambda<Func<T, bool>>(func.Body, func.Parameters[0]);
var r = data.Where(lambda.Compile());//编译表达式
return (TResult)r.GetEnumerator();
}
上述代码通过使用lambda表达式进行表达式的计算,实现了LINQ中查询的解释功能。在LINQ中,对于表达式的翻译和执行过程都是通过IQueryProvider和IQueryable<T>接口来实现的。IQueryProvider和IQueryable<T>实现用户表达式的翻译和解释,在LINQ应用程序中,通常无需通过IQueryProvider和IQueryable<T>实现自定义LINQ Provider,因为LINQ已经提供强大表达式查询和计算功能。了解IQueryProvider和IQueryable<T>接口有助于了解LINQ内部是如何执行的。
20.2.5 LINQ相关的命名空间
LINQ开发为开发人员提供了便利,可以让开发人员以统一的方式对IEnumerable<T>接口的对象、数据库、数据集以及XML文档进行访问。从整体上来说,LINQ是这一系列访问技术的统称,对于不同的数据库和对象都有自己的LINQ名称,例如LINQ to SQL、LINQ to Object等等。当使用LINQ操作不同的对象时,可能使用不同的命名空间。常用的命名空间如下所示。
1)System.Data.Linq: 该命名空间包含支持与 LINQ to SQL 应用程序中的关系数据库进行交互的类。
2)System.Data.Linq.Mapping:该命名空间包含用于生成表示关系数据库的结构和内容的 LINQ to SQL 对象模型的类。
3)System.Data.Linq.SqlClient:该命名空间包含与SQL Server 进行通信的提供程序类,以及包含查询帮助器方法的类。
4)System.Linq:该命名空间提供支持使用语言集成查询 (LINQ)进行查询的类和接口。
5)System.Linq.Expression:该命名空间包含一些类、接口和枚举,它们使语言级别的代码表达式能够表示为表达式树形式的对象。
6)System.Xml.Linq:包含 LINQ to XML 的类,LINQ to XML 是内存中的 XML 编程接口。
LINQ中常用的命名空间为开发人员提供LINQ到数据库和对象的简单的解决方案,开发人员能够通过这些命名空间提供的类进行数据查询和整理,这些命名空间统一了相应的对象的查询方法,如数据集和数据库都可以使用类似的LINQ语句进行查询操作。
加支付宝好友偷能量挖...