Entity Framework 4 in Action读书笔记——第四章:使用LINQ to Entities查询:排序和连接数据
4.4排序(Sorting)
基本需求:用户想数据根据送货城市和邮政编码排序。
解决方案:知道LINQ有一个扩展方法可以根据一个或多个属性排序你一定会很高兴。LINQ to Entities提供了这个方法的实现也一点也不奇怪。在C#中,使用orderby子句。这个子句接受要根据排序的属性。默认情况下,是升序,但是可以在属性后边加上关键字descending实现降序。如果有多个属性是降序,你可以在每个属性后边都加上descending关键字。如SQL中,尽管默认是升序,你依然可以使用ascending指定。下面是例子。
var result = from o in ctx.Orders orderby o.ShippingAddress.City select o; var result1 = from o in ctx.Orders orderby o.ShippingAddress.City, o.ShippingAddress.ZipCode descending select o;
我们很少讨论在单独类中的排序,当你根据关联的类的属性排序上将变得更有趣。
4.4.1关联排序
现在用户想在表格的顶部展示最有价值的订单。在本例中,排序根据聚合值而不是单个字段。
幸运的是,这种情况并不复杂,你已经了解了所有的基础。
var result = from o in ctx.Orders orderby o.OrderDetails.Sum(d => d.Quantity * (d.UnitPrice - d.Discount)) select new { o.OrderId, o.OrderDate, o.ShippingAddress, Total = o.OrderDetails.Sum(d => d.Quantity * (d.UnitPrice - d.Discount)) };
这个查询的结果是IOrderedQueryable<T>的对象。因为IOrdered-Queryable<T>实现了IEnumerable<T>,它可以使用foreach遍历,数据绑定操作,或另外的枚举机制。
关联数据也可以排序。执行这一操作的唯一方式是使用投影,在匿名类型中创建一个包含排序数据的属性。例如,你可能想查询订单和它们的详细信息,根据数量排序,并绑定到表格中。
当涉及到单独关联时,就更简单了,因为没有聚合需要执行,你可以使用外部属性好像是查询类的属性一样。下面展示的代码片段,查询所有订单,根据顾客所在城市排序。
var result = from o in ctx.Orders orderby o.Customer.ShippingAddress.City select o;
我们提到在查询中涉及到关联实体时SQL生成器使用映射信息处理表之间的关联。有些情况,当外键不足够关联表时,就必须使用其他列。这种情况下,必须手动处理,重写默认的行为。下一部分,我们讨论这个主题。
4.5 连接数据(Join Data)
当写跨越相关表的查询时,这些连结由SQL生成器自动处理,你不必担心。但是,属性之间存在关系的情况不能使用外键。这种情况下,你可以使用join子句。
我们还没有遇到采取连结的情况。模型的灵活特性和LINQ to Entities的查询能力使连结变得几乎没有用。
下面让我们看一个实例,说明手动连结有多不重要。假设你要查找送货城市和用户所在城市相同的订单。在SQL中,你将使用CompanyId和ShippingCity列连结Order和Company表。在LINQ to Entities中,方法一样,因为当你手动连结对象时,SQL生成器忽略类间的关系。你可以写如下的查询:
单个属性连结
var result = from o in ctx.Orders join c in ctx.Companies.OfType<Customer>() on o.ShippingAddress.City equals c.ShippingAddress.City select o;
多个属性连结
var result = from o in ctx.Orders join c in ctx.Companies.OfType<Customer>() on new { o.ShippingAddress.City, o.Customer.CompanyId } equals new { c.ShippingAddress.City, c.CompanyId } select o;
从上面的代码中可以看出,涉及一个属性的连结和多个属性的连结没有多大区别。在第一个第一段代码中,直接使用需要的属性名称,第二段代码中,你必须创建一个匿名类型,把所有连结的属性放入其中。
上面的代码运行的很好,但是使用Where子句join很容易忽略。这就需要写更少的代码以此保持简洁和可读:
var result = from o in ctx.Orders where o.Customer.ShippingAddress.City == o.ShippingAddress.City select o;
不是说一定不使用连结,而是一个精心设计设计模型只需要连结适用于一些始终被评估的特殊情况下。
只返回用户数据对应于join子句中表达的筛选条件的订单.然而,特殊情况下,你可能需要返回即使用户数据不对应于join子句中表达的筛选条件的订单。使用SQL获得这一结果需要使用OUTER JOIN,但是在LINQ to Entities,你必须采用goup join,如下面的代码:
var result = from o in ctx.Orders join c in ctx.Companies.OfType<Customer>() on new { o.ShippingAddress.City, o.Customer.CompanyId } equals new { c.ShippingAddress.City, c.CompanyId } into g from item in g.DefaultIfEmpty() select o;
真实世界的模型使用继承,OrderIT也不例外。客户/供应商和产品的情况很大程度上依赖于这个功能,使用继承查询充满复杂性,但LINQ to Entities再次派上了用场。这并不容易,但是当你学会了如何避免陷阱,你就可以跳过这一关。