.net 3.5 数据库开发 之 LINQ 上
在学习LINQ之前,有几个概念,需要澄清:
LINQ/LINQ Framework/LINQ to SQL/LINQ to …
LINQ全称为"Language-Integrated Query",是将查询功能和程序语言结合,让开发人员不需要对不同的数据源学习不同的语法。
LINQ是一种查询方式,也是一种查询语言,是程序语言的一部分。而LINQ Framework是一组与语言无关,内建于.NET Framework3.5 中的一个类库,里面定义了常见的查询函数,可以通过程序集,对对象集、数据库、XML等数据源进行查询操作。
LINQ to DataSet
DataSet 是赖以生成 ADO.NET 的断开连接式编程模型的关键元素,使用非常广泛。LINQ to DataSet 使开发人员能够通过使用许多其他数据源可用的同样的查询表述机制在 DataSet 中内置更丰富的查询功能。有关更多信息,请参见 LINQ to DataSet。
LINQ to SQL
LINQ to SQL 是适合不需要映射到概念模型的开发人员使用的有用工具。通过使用 LINQ to SQL,您可以直接在现有数据库架构上直接使用 LINQ 编程模型。LINQ to SQL 使开发人员能够生成表示数据的 .NET Framework 类。这些生成的类直接映射到数据库表、视图、存储过程和用户定义的函数,而不映射到概念数据模型。
使用 LINQ to SQL 时,除了其他数据源(如 XML)外,开发人员还可以使用与内存集合和 DataSet 相同的 LINQ 编程模式直接编写针对存储架构的代码。有关更多信息,请参见 LINQ to SQL。
LINQ to Entities
大多数应用程序目前是在关系数据库之上编写的。有时这些应用程序将需要与以关系形式表示的数据进行交互。数据库架构并不总是构建应用程序的理想选择,并且应用程序的概念模型与数据库的逻辑模型不同。实体数据模型是可用于对特定域的数据进行建模的概念数据模型,以便应用程序可作为对象与数据进行交互。有关更多信息,请参见 ADO.NET Entity Framework。
通过 实体数据模型,在 .NET 环境中将关系数据作为对象公开。这样,对象层就成为 LINQ 支持的理想目标,从而允许开发人员通过用于构建业务逻辑的语言编写对数据库的查询。此项功能称为 LINQ to Entities。有关更多信息,请参见 LINQ to Entities。
下面,举例来学习LINQ:
/// <summary>
/// LINQ to Objects查询
/// </summary>
public void LinqToObjects()
{
People people1 = new People { ID = 1, City = "Suzhou" };
People people2 = new People { ID = 2, City = "Suzhou" };
People people3 = new People { ID = 3, City = "Nanjing" };
People[] peoples = new People[] { people1, people2, people3 };
var query = from ppl in peoples
group ppl by ppl.City into pplGroup
where pplGroup.Count() > 1
orderby pplGroup.Key
select pplGroup;
foreach (var pplGroup in query)
{
foreach (People ppl in pplGroup)
{
Console.WriteLine("ID: {0}"tCity: {1}", ppl.ID, ppl.City);
}
}
}
对于LINQ来说,XML、Objects、Database都可以作为数据源,这里以Objects即对象集合作为数据源。
在上面的示例中,首先创建了一个People的对象集合,然后对该集合进行查询,最后显示查询结果
var:C#3.0中的关键字,表示后决议类型,由右边表达式决定其类型。如果没有var关键字,那么在写查询语句之前就要先思考查询后返回什么类型,这样就会很不方便,所以var的出现就为写LINQ程序带来了很大的方便。
from...where...(group..by/orderby)...select...:看起来很像SQL语句,只是将select放在了最后,为甚会这样呢?其实想想,这样也挺合理的,要选择结果,当让应该先知道数据源,知道数据源后确定过滤条件进行过滤,最后在选出所要的结果,这样更加符合逻辑。Anders Hejlsberg在一次访谈中提及,他认为SELECT和FROM的位置不对,直觉上应该是要把FROM摆在前面,SELECT摆在后面才对。
from后面跟的是数据源,格式为<alias> in Objects,在LINQ to Objects中属于源为Objects,而在LINQ to SQL中,数据源为数据库。
where后跟条件,从Reflector反组译的结果可以看出,where被组译成peoples.Where<People>([delegate]),即调用peoples的Where函数,但是peoples为数组,并没有Where方法,为什么这里有Where函数调用?这主要是C#3.0里的另一个新技术“Extension Method”(扩充方法)。
Extension Method:扩充方法,即为已存在的类添加新的方法。Extension Method必须声明在一个非泛型的静态类中,而且必须要声明为静态函数,其第一个参数就是想Extension的类型,并且要以this语句作为标识符,如:
public static class MyExtensionMethod
{
public static int GetLength(this string str)
{
return str.Length;
}
}
Extension Method的Generics Type Parameter assumption(泛型类型参数推演):当扩展方法为泛型方法时,对该方法的调用,如果没有确定泛型的类型,编译器会自动假设调用端类型为函数的泛型类型,如
public static class MyExtensionMethod
{
public static string ShowResult<T>(this T str)
{
return str.ToString();
}
}
一般来说,在调用泛型函数时,必须指定类型参数,如对ShowResult的调用:ShowResult<string>();
但是people.ShowResult()的调用方式也能被编译器所接受,为什么呢?这是应为C#3.0编译器会为Extension Method调用一种叫做泛型类型推演的操作,也就是由调用端假设被调用端的类型参数。
Lambda Expression:Lambda表达式。在上面也说过,当我们使用LINQ表达式时,编译器会将LINQ表达式专程调用这些Extension Method的程序代码,如where转换成Where扩展方法,select 转化成Select方法等,那么我们能不能不通过LINQ Expression,而直接调用扩展方法呢?
当然可以,如下例:
class program
{
static void Main(string[] args)
{
var lists = new string[]{"first", "second", "third"};
var result = list.Where(o => o == "first");
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
其中,加粗的部分即为Lambda表达式,它就相当于一个匿名委托,如 Where(deletegate(string o){ return o == "first"; });
Lambda Expression的引入,使匿名委托变得非常简单。在Lambda表达式中,【=>】左方是delegate的参数名称,右方的是程序代码,如果右边代码比较复杂,可放在{}中,如:Where(o => {Console.WriteLine(o); return o == "first";});