(LINQ 学习系列)(2)LINQ to SQL 查询
定义 LINQ to SQL 查询所用的语法与在 LINQ 中使用的语法相同。唯一的差异是您的查询中引用的对象映射到数据库中的元素。
查询执行关系图
下表显示了 LINQ 与 LINQ to SQL 查询项之间的相似和不同之处。
项 |
LINQ 查询 |
LINQ to SQL 查询 |
保存查询的局部变量的返回类型(对于返回序列的查询而言) |
泛型 IEnumerable |
泛型 IQueryable |
指定数据源 |
使用 From (Visual Basic) 或 from (C#) 子句 |
相同 |
筛选 |
使用 Where/ where 子句 |
相同 |
分组 |
使用 Group…By/ groupby 子句 |
相同 |
选择(投影) |
使用 Select/ select 子句 |
相同 |
延迟执行与立即执行 |
请参见 LINQ 查询简介 (C#) |
相同 |
实现联接 |
使用 Join/ join 子句 |
可以使用 Join/ join 子句,但使用 AssociationAttribute 属性更有效。 有关更多信息,请参见 跨关系查询 (LINQ to SQL)。 |
远程执行与本地执行 |
|
有关更多信息,请参见 远程查询执行与本地查询执行 (LINQ to SQL)。 |
流式查询与缓存查询 |
在本地内存情况中不适用 |
|
基本 LINQ 查询操作 (C#)
获取数据源
在 LINQ 查询中,第一步是指定数据源。像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用它。在 LINQ 查询中,最先使用 from 子句的目的是引入数据源 ( customers) 和范围变量 ( cust)。
var q = from c in customers
select c.OID,c.customername;
筛选
也许最常用的查询操作是应用布尔表达式形式的筛选器。此筛选器使查询只返回那些表达式结果为 true 的元素。使用 where 子句生成结果。 实际上,筛选器指定从源序列中排除哪些元素。
var q = from c in customers
where c.City == "London"
select c;
Where 操作中连接符号 && (与) || (或) ! (非)
排序 orderby c.Name ascending
. 返回集合中的第一个元素 相对于 Top 1
var q=db.GetTable<student>().First<student>();
. 查询不重复的结果
var q=db.GetTable<student>().Distinct<student>();
分组 group by
在使用 group 子句结束查询时,结果采用列表的列表形式。 列表中的每个元素是一个具有 Key 成员及根据该键分组的元素列表的对象。 在循环访问生成组序列的查询时,您必须使用嵌套的 foreach 循环。 外部循环用于循环访问每个组,内部循环用于循环访问每个组的成员。
var q = from c in customers
group c by c.City;
// customerGroup is an IGrouping<string, Customer>
foreach (var customerGroup in q)
{
Console.WriteLine(customerGroup.Key);
foreach (Customer customer in customerGroup)
{
Console.WriteLine(" {0}", customer.Name);
}
}
联接
var q = from c in db.student
join o in db.@class on c.StudentName equals o.Student
select new { c.ID ,c.StudentName ,o.Class1 };
选择(投影)
select 子句生成查询结果并指定每个返回的元素的“形状”或类型。 例如,您可以指定结果包含的是整个 Customer 对象、仅一个成员、成员的子集,还是某个基于计算或新对象创建的完全不同的结果类型。 当 select 子句生成除源元素副本以外的内容时,该操作称为“投影”。 使用投影转换数据是 LINQ 查询表达式的一种强大功能。
2.重写 Insert、Update 和 Delete 的默认行为。
LINQ to SQL 不强制满足以下要求,但如果这些要求未得到满足,就会导致行为不明确。
· 重写方法不能调用 SubmitChanges 或 Attach。 如果在重写方法中调用这些方法,LINQ to SQL 会引发异常。
· 重写方法不能用来启动、提交或停止事务。SubmitChanges 操作以事务的形式执行。 内嵌的事务可能会干扰外层事务。加载重写方法只能在它们确定 Transaction 中未在执行相应操作后启动事务。
· 重写方法应遵循适用的开放式并发映射。发生开放式并发冲突时,重写方法应引发 ChangeConflictException。 LINQ to SQL 会捕获此异常,以便您可以正确处理 SubmitChanges 时提供的 SubmitChanges 选项。
· Create ( Insert) 和 Update 重写方法在相应操作成功完成后应使数据库生成的列的值流回对应的对象成员。
例如,如果 Order.OrderID 映射到标识列(autoincrement 主键),则 InsertOrder() 重写方法必须检索数据库生成的 ID 并将 Order.OrderID 成员设置为该 ID。 同样,时间戳成员必须更新为数据库生成的时间戳值,以确保更新后的对象一致。如果未能传播数据库生成的值,则会造成数据库与 DataContext 跟踪的对象之间不一致。
· 调用正确的动态 API 是用户的责任。例如,在更新重写方法中,只能调用 ExecuteDynamicUpdate。 LINQ to SQL 不检测或验证调用的动态方法是否与适用的操作相匹配。如果调用了不适用的方法(例如,为要更新的对象调用了 ExecuteDynamicDelete),则结果是不明确的。
· 最后,重写方法应执行明确的操作。LINQ to SQL 操作(如预先加载、延迟加载和 SubmitChanges))的语义要求重写提供明确的服务。例如,只返回空集合而不检查数据库内容的加载重写有可能会造成数据不一致。
3.设置适当的选项以检测和报告并发冲突。
在 LINQ to SQL 对象模型中,当以下两个条件都得到满足时,就会发生“开放式并发冲突”:
· 客户端尝试向数据库提交更改。
· 数据库中的一个或多个更新检查值自客户端上次读取它们以来已得到更新。
此冲突的解决过程包括查明对象的哪些成员发生冲突,然后决定您希望如何进行处理。
冲突检测和解决检查表
您可以检测和解决任意详细等级的冲突。一种极端情况是,您可以用三种方式之一(请参见 RefreshMode)来解决所有冲突,而不再作其他方面的考虑。 另一种极端情况是,您可以为发生冲突的每个成员上的每种冲突指定特定操作。
- 在您的对象模型中指定或修改 UpdateCheck 选项。
- 在对 SubmitChanges 的调用的 try/catch 块中,指定您希望在哪个点引发异常。
- 决定您希望检索的冲突详细信息量,并在 try/catch 块中包括相应的代码。
- 在 try/ catch 代码中包括您希望解决您发现的各种冲突的方式。
术语 |
说明 |
并发 |
两个或更多用户同时尝试更新同一数据库行的情形。 |
并发冲突 |
两个或更多用户同时尝试向一行的一列或多列提交冲突值的情形。 |
并发控制 |
用于解决并发冲突的技术。 |
开放式并发控制 |
先调查其他事务是否已更改了行中的值,再允许提交更改的技术。 相比之下,保守式并发控制则是通过锁定记录来避免发生并发冲突。 之所以称作开放式控制,是因为它将一个事务干扰另一事务视为不太可能发生。 |
冲突解决 |
通过重新查询数据库刷新出现冲突的项,然后协调差异的过程。 刷新对象时,LINQ to SQL 更改跟踪器会保留以下数据:
LINQ to SQL 随后会确定相应对象是否发生冲突(即它的一个或多个成员值是否已发生更改)。如果此对象发生冲突,LINQ to SQL 下一步会确定它的哪些成员发生冲突。 LINQ to SQL 发现的任何成员冲突都会添加到冲突列表中。 |