提示6. 如何及何时使用贪婪加载

什么时候你需要使用贪婪加载?

通常在你的程序中你知道对查询到的实体将要进行怎样的操作。

例如,如果你查询一个订单以便为一个客户重新打印,你知道没有组成订单的项目即产品的信息重打印将是不完整的,所以你知道你将需要同时加载这些信息。

这是贪婪加载起作用的一类场景。

如果你知道你需要额外信息,或实体,你可能也会预先加载这些实体(贪婪加载),因为这将省下生在将来的查询。

怎样进行贪婪加载?

与一些普遍存在的错误观念相反,Entity Framework中贪婪加载即可行也易行,你仅使用Include()方法如下这样启动查询即可:

1 var reprint = (from order in ctx.Orders.Include("Items.Product")
2 where order.Customer.Name == "Fred Blogs"
3 && order.Status == "Unshipped"
4 select order).First();

这个查询意思是,在每个满足查询条件的order中包含其"Items",同时每个Item中也包含其"Product".

这样的结果是,如下代码:

1 foreach (var item in reprint.Items)
2 {
3 Console.WriteLine("\t{0} {1} = ${2}",
4 item.Quantity,
5 item.Product.Name,
6 item.Cost);
7 }
8 Console.WriteLine(reprint.TotalCost);

不再需要新的查询。

注意:

不需要显式 Include("Items") ,对 Include("Items.Product") 的调用会隐式包含Items。

提示7 – 怎么在.NET 3.5 SP1中模拟外键属性

背景

如果你一直关注EF Design Blog这个团队博客,你可能已经看到近期我们宣布了一个用于.NET 4.0中EF的称为FK Associations的特性。

然而在.NET 3.5 SP1中,我们仅支持独立关联。这表示外键列不会做为可用的属性出现在实体中。相反这表示你不得不通过到其它实体的引用来建立关联。

例如,不同于Linq to SQL,下面的写法在EF中不可用:

1 product.CategoryID = 2; 

因为在product实体中没有"CategoryID"这个属性。

你不得不使用类似下面的替换方法:

1 product.Category = ctx.Categories.First(c => c.ID == 2); 

也就是你需要使用Category引用。这意味着你不得不在内存中放置一个Category对象,或者像上面例子那样通过查询获取一个。

不存在一个你可以直接设置CategoryID的属性的问题可能造成很大的麻烦,而这就是我们在.NET 4.0中添加了FK Associations与FK Properties的原因。

这看起来都不错,但Julie Lerman总习惯提醒我,现在正在使用.NET 3.5 SP1的人们怎么办?我们的新特性现在帮不上忙。

所以…

怎样在CategoryID不实际存在的情况下设置它?

这问题是个圈套。你当然实现不了这种想法。

但是你可以使用下面的代码实现同样的效果:

1 product.CategoryReference.EntityKey = new EntityKey("MyContainer.Categories", "ID", 2); 

这看起来有点复杂不是吗?所以让我们拆开分析:

1. 对于每一个像Category一样的引用"xxx",Entity Framework也生成了一个返回EntityReference类型的"xxxReference"属性。(EF中大量使用EntityReference来提供完成像保持关系一致性,及基于EntityKeys的实体查找等操作所需的设施与服务)

2. EntityReference的其中一个属性是EntityKey。当我们希望改变外键的值时我们需要将一个新的EntityKey设置到EntityKey属性。

3. 要创建一个新的EntityKey,我们需要知道如下3条:

1). 我们想要设置给"外键"的值。与前面代码段一致,这个值就是2。

2). 目标Entity所属的EntitySet的完成限定名。在这个例子中,我们的目标是一个Category,我们由"MyContainer.Categories"集来获取Category。

3). 目标实体中主键属性的名称。这个例子中Category的主键为"ID"属性。

很显然这不是你想要在你需要的任何地方书写的那种类型的代码。所以我们以这种方式来封装。以便…

怎样使用这些代码来仿造一个外键属性?

幸运的是Entity Framework为实体生成了部分类,所以你可以像这样简单的将这些逻辑放置于你自己的Product部分类中:

 1 public int CategoryID {
2 get {
3 if (this.CategoryReference.EntityKey == null) return 0;
4 else return (int) this.CategoryReference
5 .EntityKey.EntityKeyValues[0].Value;
6 }
7 set {
8 this.CategoryReference.EntityKey
9 = new EntityKey("MyContainer.Categories", "ID", value);
10 }
11 }

注意我们在get访问器中也使用了CategoryReference。这样我们的CategoryID属性仅就是一个基于CategoryReference的视图,这样如果EF在底层做了任何改变我们也无需被通知。

添加这样的代码后,之前我们想要的写法也就可以实现:

1 product.CategoryID = 2; 

这在做法在很多场景中都特别有用,例如MVC控制器与数据绑定中。

外传

这种解决方案本质上仅仅是隐藏了独立关联的一些限制的变通方案,同所有的变通方案其也存在缺陷。关键就是在优点与缺陷之间做出全面的权衡。一些关键的缺陷如下:

1. 部分类中的属性不会被Entity Framework所识别,所以你不能在LINQ查询中使用它们。

2. 由于在setter访问器中我们通过完全限定名引用了EntitySet,我们将Entity Class与EntitySet直接耦合在一起。对于大多数人这不成问题,但是当你试图在不同上下文间重用你的实体类型或使用MEST(MultipleEntitySets per Type)时这种方法不会工作。

3. 可能还有更多缺陷…当想到它们时我将陆续添加!

提示8 – 怎样使用LINQ to Entities编写"WHERE IN"形式的查询

想象你有一个人员表,你想查询其中姓氏包含于一个有趣的姓氏列表中的那部分人。在SQL中这很平常,你编写这样的查询:

1 SELECT * FROM People
2 WHERE Firstname IN ('Alex', 'Colin', 'Danny', 'Diego')

SQL中的IN等价于LINQ中的Contains

在LINQ(to objects)的世界不存在'IN',所以你需要像倒置顺序那样来使用Contains方法:

1 var names = new string[] { "Alex", "Colin", "Danny", "Diego" };
2 var matches = from person in people
3 where names.Contains(person.Firstname)
4 select person;

注意语义上我们已经由SQL中的:

value.IN(set)

变为LINQ中的

set.Contains(value)

结果是相同的。

.NET 3.5 SP1与.NET 4.0对Contains的支持

在.NET 4.0的EF中将支持 IEnumerable<T>.Contains(T t) 方法,所以在下个版本EF中你可以编写上文LINQ查询那样的查询。

不幸的是这种写法不被.NET 3.5 SP1支持:当遇到这种形式的LINQ表达式时LINQ to Entities会报错,因为其不知道怎样将对Contains的调用转换为SQL。

但是我们.NET 3.5 SP1的用户怎么办呢?他们应该怎样做?

.NET 3.5 SP1的变通方案

有一个由EF团队"大脑"之一Colin提供的变通方案。

这种变通方案的本质是你可以以如下方式来重写上面的查询:

1 var matches = from person in people
2 where person.Firstname == "Alex" ||
3 person.Firstname == "Colin" ||
4 person.Firstname == "Danny" ||
5 person.Firstname == "Diego"
6 select person;

当然这会使代码变长且编写这样的代码是痛苦的,但是它同样可以工作。

所以如果有一些工具方法可以很容易的创建这种类型的LINQ表达式,我们将很好的处理业务

而恰巧不久之前Colin在论坛上给出了他完成的帮助方法,借助他的方法,你可以如下这样编写查询:

1 var matches = ctx.People.Where(
2 BuildOrExpression<People, string>(
3 p => p.Firstname, names
4 )
5 );

这会生成一个与下面语句相同效果的查询:

1 var matches = from p in ctx.People
2 where names.Contains(p.Firstname)
3 select p;

但事实上后者一个明显的问题是其不可以在.NET 3.5 SP1下工作。

外传

如果你已经阅读至此,很好!

下面是使变通写法成为可能的功能函数:

 1 public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
2 Expression<Func<TElement, TValue>> valueSelector,
3 IEnumerable<TValue> values
4 )
5 {
6 if (null == valueSelector)
7 throw new ArgumentNullException("valueSelector");
8 if (null == values)
9 throw new ArgumentNullException("values");
10 ParameterExpression p = valueSelector.Parameters.Single();
11 if (!values.Any())
12 return e => false;
13 var equals = values.Select(value =>
14 (Expression)Expression.Equal(
15 valueSelector.Body,
16 Expression.Constant(
17 value,
18 typeof(TValue)
19 )
20 )
21 );
22 var body = equals.Aggregate<Expression>(
23 (accumulate, equal) => Expression.Or(accumulate, equal)
24 );
25
26 return Expression.Lambda<Func<TElement, bool>>(body, p);
27 }

此函数本质上构造了一个针对所有使用 valueSelector (i.e. p => p.Firstname) 的值断言表达式,并将这些断言进行OR连接来为这个完整的预言创建一个表达式,除此之外,我不打算试图解释这个函数。

Entity Framework技巧系列之二 - Tip 6 - 8的更多相关文章

  1. Entity Framework技巧系列之十三 - Tip 51 - 55

    提示51. 怎样由任意形式的流中加载EF元数据 在提示45中我展示了怎样在运行时生成一个连接字符串,这相当漂亮. 其问题在于它依赖于元数据文件(.csdl .ssdl .msl)存在于本地磁盘上. 但 ...

  2. (翻译)Entity Framework技巧系列之十 - Tip 37 - 41

    提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...

  3. Entity Framework技巧系列之十一 - Tip 42 - 45

    提示42. 怎样使用Code-Only创建一个动态模型 背景: 当我们给出使用Code-Only的例子,总是由创建一个继承自ObjectContext的强类型的Context开始.这个类用于引导模型. ...

  4. Entity Framework技巧系列之十 - Tip 37 - 41

    提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...

  5. Entity Framework技巧系列之九 - Tip 35 - 36

    提示35. 怎样实现OfTypeOnly<TEntity>()这样的写法 如果你编写这样LINQ to Entities查询: 1 var results = from c in ctx. ...

  6. Entity Framework技巧系列之五 - Tip 16 – 19

    提示16. 当前如何模拟.NET 4.0的ObjectSet<T> 背景: 当前要成为一名EF的高级用户,你确实需要熟悉EntitySet.例如,你需要理解EntitySet以便使用 At ...

  7. Entity Framework技巧系列之六 - Tip 20 – 25

    提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你 ...

  8. Entity Framework技巧系列之十二 - Tip 46 - 50

    提示46. 怎样使用Code-Only排除一个属性  这次是一个真正简单的问题,由StackOverflow上这个问题引出.  问题:  当我们使用Code-Only把一个类的信息告诉Entity F ...

  9. Entity Framework技巧系列之十四 - Tip 56

    提示56. 使用反射提供程序编写一个OData Service 在TechEd我收到一大堆有关将数据作为OData暴露的问题. 到目前为止你大概知道可以使用数据服务与Entity Framework将 ...

随机推荐

  1. php入门 数据类型 运算符 语言结构语句 函数 类与面向对象

    php PHP-enabled web pages are treated just like regular HTML pages and you can create and edit them ...

  2. 为XYplorer添加右键菜单:“使用XYplorer打开”

    在目录.磁盘右键添加: Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\Shell\XYplorer]"E ...

  3. (转载)#include机制,#ifndef...#define...#endif防止重复引用,声明,定义等概念

    一.来自百度知道的问题:全局变量可以声明定义在头文件中? 答案: 注意头文件中不可以放变量的定义!!!一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include).如果把定义放到 ...

  4. Design Pattern——开放封闭原则

    两个特征: 1.对于扩展是开放的 2.对于更改是封闭的 意思就是说:程序在设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果有新的需求来,我们就增加一个类来解决问题,而不要更改原 ...

  5. 第二章App框架设计与重构

    response标准格式: { "isError":false, "errorType": 0, "errorMessage": " ...

  6. centsOS下安装vsftp的配置

    1. 添加用户组 # groupadd www 2. 修改配置 # vi /etc/vsftpd/vsftpd.conf 查找: #chroot_list_enable=YES #chroot_lis ...

  7. 第七十八节,CSS3文本效果

    CSS3文本效果 一.文本阴影 CSS3提供了text-shadow文本阴影效果,这个属性在之前讲过,只是没有涉及浏览器 支持情况. 浏览器支持情况 text-shadow       Opera   ...

  8. javascript生成新标签的三种方法

    javascript生成新标签的三种方法:http://www.cnblogs.com/online-link/p/6062423.html

  9. 8、Spring+Struts2+MyBaits(Spring注解+jdbc属性文件+log4j属性文件)

    一.注解理论 使用注解来构造IoC容器 用注解来向Spring容器注册Bean.需要在applicationContext.xml中注册<context:component-scan base- ...

  10. WildMagic 简单图元(一)

    #include <Wm5WindowApplication3.h> #include <Wm5VisualEffectInstance.h> using namespace ...