Entity Framework 4 in Action读书笔记——第四章:使用LINQ to Entities查询:继承查询

继承引入了多态查询的概念。这种类型的查询认为继承层次结构和返回的对象可能是不同类型,但是继承自相同的基类。

假设你想查询所有的产品。从多态查询获得的是Product对象列表,但具体的类型是Shirt或者Shoe,因为引擎自动实例化为正确的类型。你不仅可以自动得到正确的类型,还可以根据类型应用过滤。例如,你可以只查询鞋子或者只查询衬衫。

我们敬爱的beta测试用户终于对OrderIT订单的展示满意了,现在用户想把注意力放在产品上。首先,他们想在单页中查看所有的产品。这非常简单:

var result = from p in ctx.Products
             select p;

这段代码的结果是Product对象的列表,但是真实的类型是它具体的继承类。

接下来用户想根据产品的类型应用筛选。LINQ提供两种方式指定类型:类型相等运算符(is)和OfType方法。这两种方法有一个重要的区别。相等运算符执行筛选操作,然而OfType<T>方法不仅筛选,还转换为需要查询的类型。下面的代码更清楚:

IEnumerable<Product> products = from p in ctx.Products
                                where p is Shoe
                                select p;
IEnumerable<Shoe> shoes = from p in ctx.Products.OfType<Shoe>()
                          select p;

由于这种微小的差别,选择哪种方法完全是个人问题。假设你有几个产品类型,你需要找到唯一的鞋和衬衫。

在这个例子中,使用OfType<T>是可能的,但是太复杂,因为你必须合并两个查询来检索不同的对象。合并结果之前,你必须转换内在对象成Product基类。相比之下,相等运算符不需要修改最后的结果,所以过滤后你不需要做额外的工作。

在其他情况下,你可能想只搜索一个具体的产品。在种情况下,使用OfType<T>方法是最好的选择。

对基类的属性筛选可以使用同样的方式。用户现在想对继承类型的数据筛选,例如,用户需要所有Sport属性包含Basket的鞋子。这是另一个适用OfType<T>的情况。因为OfType<T>转换数据,where方法(放在OfType<T>方法之后)已经知道输出类型是Shoe,所以这个搜索非常简单:

var result = from p in ctx.Products.OfType<Shoe>()
             where p.Sport == "Basket"
             select p;

但是如果你想找到所有的“basket”的鞋子而且以列表的方式返回Product对象呢?这种情况,前面的代码就不起作用了,因为它返回Shoe对象的列表。有两个解决方法:使用LINQ的Cast<T>方法,它转换回Product,或者在Where子句中转换Object成所需的类型,然后应用筛选。第二种,不能使用显示转换,那将导致一个运行时异常。你必须采用as转换操作符。

第一种的代码:

var result = from p in ctx.Products
             where (p as Shoe).Sport == "Basket"
             select p;

第二种代码:

var result = (from p in ctx.Products.OfType<Shoe>()
              where p.Sport == "Basket"
              select p).Cast<Product>();

第一种方法,所需的类型是Product,所以所有的产品被扫描但是只有“Basket”的鞋子返回。生成的SQL也是按照相同的途径:它扫描所有的Product,Shoe和Shirt表,使用select case SQL子句识别一行是shoe还是shirt,浪费了很多资源,因为你不需要从Shirt表中获取数据。

第二种方法,因为它直接规定了所需的类型是Shoe,生成的SQL只是用Product和Shoe表,优化了查询性能。除非你有强大的动力在Where中使用转换,否则最好始终使用OfType<T>方法。

还有另一个性能说明要铭记于心。考虑下面的代码:

var result = from p in ctx.Products
             select p.Name;

所涉及的唯一列是Product表中的Name。你可能期望生成的SQL只查询那个表。但SQL在层次结构(Shirt和Shoe)上和涉及到的表执行外连接。这种情况下,存储过程是最好的方法。

到目前为止,你已经使用了标准的LINQ方法。他们很强大,但它们不包括所有的查询可能性。LINQ to Entities允许你应用任何的CLR方法,但是SQL转换引擎并不能理解一切。更重要的是,你可能有一个数据库函数或者自定义函数在LINQ to Entities查询中很有用,但是没有办法使用这些函数。这就是EF4.0函数功能发挥作用的地方

posted @ 2011-09-03 21:48  BobTian  阅读(2895)  评论(8编辑  收藏  举报