从本质上讲,Linq-to-SQL能够将类与方法映射到数据源对象上。

LINQ概述

  大多数应用程序以某种数据仓库为中心。多年来,架构师在设计应用程序的过程中,一直通过对象对问题域进行建模。这些对象包括用于连接到数据访问层的连接,来艰难地与数据库进行交互,并通过对象模型建立关系模式。但这种方式对于简单的应用程序显得有些繁琐。

  LINQ的出现就是为了解决这些简单应用程序的需求,为其提供一套功能强大且易用的工具,能在更高的概念层上对数据存储进行操作。

  .NET 3.5版本中的编程语言能进行局部类型推断,var关键字正是为此而引入的。从本质上讲,它允许我们声明一个变量,而不指定实现的类型。编译器会根据赋给该变量的值来推断其实际的类型。类型推断不涉及装箱与拆箱这类运行时的操作。所有工作完全由编译器来完成,也就是在代码执行前完成。编译器会识别我们使用的实际类型,并在中间语言代码中以具体的、类型安全的方式声明该变量。局部类型推断仅限于局部变量,我们不能以这种方式声明成员变量类型,也不能在方法原型中使用。

  Linq-to-SQL是一种O/R实现,能通过.NET类对SQL Server数据库进行建模。Linq-to-SQL框架针对的是SQL Server,不能用于处理其他关系型数据库。

通用的查询语法

  LINQ表达式即可对支持的数据源进行查询,也可对其更新。LINQ表达式中出现的所有变量都是强类型。

  有两种方式来表达LINQ操作,其一是基于新的语言构造(如:from、select、where)来表达查询,其二是通过Lambda方法的调用。注意:所有查询表达式是在编译时映射到方法上的。两种方法功能等价,效率相同。如:

var data = from c in db.Customers
where c.Country == "Spain"
select c;
//等价于
var data = db.Customers.Where(c => c.Country == "Spain");

  在.NET 2.0版中,托管语言获得了一种功能强大的构造,即匿名方法。不必在委托中使用命名方法,我们可在任意位置定义匿名方法,且在匿名方法内部仍可以访问外部变量,返回值的类型可推断得到。这种方式的出现,降低了类中许多私有方法的数目。Lambda表达式是对匿名方法思想进一步的延伸,且更精炼。

投影运算符

  select运算符用于将数据源中的内容投影到内存中,常与from关键字一起使用。

  Select与SelectMany间的不同是,前者返回对象的层次型结果,后者序列型结果。比如:Customers与Orders两个表间关系为,Customers存储客户信息,Orders存储客户订单信息。看看以下代码会返回什么呢?

var data = from c in customers
where c.Country == "Spain"
select (c.Orders);
  针对满足条件的客户,它会返回相应的对象序列,每个对象是Order对象的数组。再让我们看看下面的代码:
var data= (from c in customers
where c.Country == "Spain"
select c).SelectMany(c
=> c.Orders);

  这种情况下,我们会得到一个序列,建立一对多关系的客户ID信息会重复出现。

联接与分组

  Join运算符能通过两个集合中匹配的键对二者进行内联接:

var data = from c in customers
join o
in orders
on c.CustomerID equals o.CustomerID
select
new {c.Name, o.OrderNumber};

  与SQL语言不同,在LINQ中,join的on子句区分项目的顺序,第一项必须来自外层序列,而第二项(equals运算符右侧)必须来自内层序列,否则,会出现编译错误。

  join操作符会对外层集合(上例中为customers)元素进行遍历,针对被联接的集合(上例中为orders)中条件满足的元素,向结果集中添加新元素。

  该子句也支持其他联接行为,如outer外联接。具体来说,使用outer join得到的结果表中,外层元素即使与内层元素不匹配,也会返回。

  示例代码:

var data = from c in customers
join o
in(
from orderInJan97
inorders
where orderInJan97.OrderDate.Value.Year == 1997 &&
orderInJan97.ValueDate.Value.Month
== 1
select orderInJan97
)
on c.CustomerID equals o.CustomerID into groupOrders
select
new { c.CompanyName, OrderTotal = groupOrders.Count() };

  customers集合与1997年1月的订单子集进行联接,基于每个客户的每组订单会临时地存储在groupOrders容器中。如果某组中未发现匹配的键,则该组为空。结果表的每条记录对应一个客户,分别带有客户在给定时间段下的订单总数。

  group子句会返回分组对象的序列,分组对象可能为空,但如果包含数据项,那么这些数据项的键值会与该组的键值匹配。因此,分组操作的输出不能直接绑定到表格式数据绑定控件(如GridView)。

var data = from c in customers
group c.ContactName by
new { City = c.City, Region = c.Region } into g
where g.Count() > 1
select g
  在该示例中,我们将客户城市和区域分组,并将结果添加到联系人姓名集合中。此外,我们只选择在每对城市/区域中有多个联系人的客户。注意,如果要根据多个属性进行分组,则必须使用匿名类型(new运算符)。一旦某个组被建立,如果要对分组操作应用其他限制条件(就像T-SQL中的Having子句),只需要在by子句中表达相应的条件即可。如下例只关心那些城市名称字符数大于5的记录:
var data = from c in customers
group c.ContactName by
new { City = c.City, Length = c.City.Length > 5 } into g
where g.Count() > 1
select g;
  注意,by子句后的任何语句都会被逐一处理。上述代码与下面的代码完全不同:
var data = from c in customers
group c.ContactName by
new { City = c.City.Length > 5 } into g
where g.Count() > 1
select g;

  在这种情况下,需要满足的条件是,城市名称的字符数大于5,但不会检查城市名称中的匹配项。因此,Rome中的联系人会被忽略,London和New York的联系人将被归到一组。

聚合运算

  许多操作符可对对象进行聚合和预定义的计算。LINQ提供了常用的运算符(如:Count、Sum、Average、Min和Max)。

  Sum示例:

var data = from od indataContext.Order_Details
where od.OrderID == 10250
select
new{
              od.OrderID,
              OrderAmount
= Sum(od.Quantity * od.UnitPrice)
};

分区

  我们可使用Take运算符从某序列取出指定数据的连接元素。如下所示:

var data = (from c in customers
select c).Take(
10);

  该示例只获取了customers集合中前10个对象。Take运算符不是内建的语言构造,它是一个方法,位于代表查询投影的类中。

  我们还可以使用Skip运算符跳过某序列开头指定数据的连续元素。如下所示:

var data = (from c in customers
select c).Skip(30).Take(
10);

  使用这种方法可用于分页查询。

单个元素的确定方法

  LINQ提供了First和Single运算符来分别返回每一个和单个满足条件的元素。

  First示例:

var data = (from c in dataContext.Customers
join o
in (from t in dataContext.Orders
where t.OrderDate.Value.Year == 1998 &&
t.OrderDate.Value.Month
== 5
select t)
on c.CustomerID equals o.CustomerID
select
new { Company = c.CompanyName,
Country
= c.Country,
OrderDate
= o.OrderDate }
).First(x
=> x.Country == "USA");

  这个查询将结果列表限制为指定时间内第一个下订单的美国客户。如果First的查询结果为空,则会抛出异常。为避免异常的抛出,可使用FirstOrDefault运算符取代。该运算符如未找到符合条件的记录则返回null。

  不论是有一个还是多个满足条件的对象,First运算符都能正常工作。而Single运算符只允许有一个满足条件的对象存在,否则抛出异常。SingleOrDefault运算符确保在结果为空时,不抛出异常,但如果有多个满足条件的元素,仍会抛出无效操作异常。

posted on 2011-04-19 18:07  辛勤的代码工  阅读(624)  评论(0编辑  收藏  举报