【转】【翻译】实体框架中的POCO支持 - 第二部分 - 复杂类型,延迟装载和显式装载
2009-05-29 14:13 E2Home 阅读(787) 评论(0) 编辑 收藏 举报【原文地址】POCO in the Entity Framework : Part 2 – Complex Types, Deferred Loading and Explicit Loading
【原文发表日期】 28 May 09 09:03 AM
在上星期的贴子《POCO Experience in Entity Framework》 (实体框架中的POCO体验)中,我讨论了Entity Framework 4.0中POCO支持的基本。在这个贴子里,我将讨论与POCO相关的另外几个方面。
复杂类型(Complex Types)
POCO中的复杂类型支持跟常规的基于EntityObject的实体中的复杂类型支持一样。你要做的就是将它们声明为POCO类,然后在你的POCO实体中使用和声明基于它们的属性。
作为例子,这里是一个InventoryDetail复杂类型,代表我的Product实体的一个部分:
public class InventoryDetail
{
public Int16 UnitsInStock { get; set; }
public Int16 UnitsOnOrder { get; set; }
public Int16 ReorderLevel { get; set; }
}
把我的Product类修改成包含一个这个类型的属性,用来组合几个有关库存细节的字段:
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public int SupplierID { get; set; }
public string QuantityPerUnit { get; set; }
public decimal UnitPrice { get; set; }
public InventoryDetail InventoryDetail { get; set; }
public bool Discontinued { get; set; }
public Category Category { get; set; }
}
然后你可以做你以前对复杂类型所能做的一切,这是一个查询的例子:
var outOfStockProducts = from c in context.Products
where c.InventoryDetail.UnitsInStock == 0
select c;
你可以看到,POCO中的复杂类型支持用起来非常直截了当。但在POCO中使用复杂类型支持时,你需要记住几件事情:
- 你必须将复杂类型定义为类(class),结构体(struct)是不支持的。
- 在你的复杂类型类中,你不能使用继承。
既然在讨论复杂类型,我想我要提一下另外一件事,你知道Visual Studio 2010中的实体框架设计器支持复杂类型的声明么?
在Visual Studio 2008中,你只能手工将复杂类型的声明加到CSDL中去,但随着Visual Studio 2010中的设计器中对复杂类型支持的推出,这一切都成了历史。
更酷的是,因为Visual Studio 2010支持多定向(Multi-Targeting),你可以在开发针对.NET Framework 3.5,使用了Entity Framework 3.5的应用中也使用这个功能!
延迟/懒式装载
在我2个星期前发表的《延迟装载初览》一文中,我提到了实体框架现在支持延迟装载了。默认的、代码生成的基于EntityObject的实体类型将提供延迟装载,自然毫不奇怪。如果你想知道POCO对象中是否也支持延迟装载,那么我想,你会很高兴地知道,你在POCO中也能得到延迟装载支持。
为在POCO实体中使用延迟装载支持,你需要做2件事情:
- 将你想要懒式装载的属性声明为virtual,这些属性可以是任何实现了ICollection<T> 的集合类,或者它们可以是代表了1/0..1 关系的引用。
例如,这里是更新过的Category实体类的部分代码,我将其改成支持延迟装载了:
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public byte[] Picture { get; set; }
public virtual List<Product> Products { get; set; }
...
2. 在上下文中启用延迟装载选项:
context.ContextOptions.DeferredLoadingEnabled = true;
这就行了,你现在就将得到POCO类型的自动延迟装载,而不用做任何其他什么事情。
那么这玩意到底是怎么工作的,底层是怎么进行的?
这玩意能工作的原因是因为,在我将集合类型的属性标记为virtual后,这允许实体框架在运行时为我的POCO类型提供一个代理(proxy) 实例,正是这个代理实现了自动的延迟装载。该代理实例是基于一个继承自我的POCO实体类的类型,所以你提供的所有功能都被保留下来了。从开发人员的角度来看,即使延迟装载或许是个需求,这也允许你编写透明持久性的代码。
如果你在调试器中检查实际的实例时,你会看到该实例的底层类型与我原先声明的类型是不同的:
虽然实体框架尽力以最小的摩擦提供自动的延迟装载,但在处理你想要添加或附加手工生成的实例时,或者当你序列化/发序列化实例时,这是你需要知道的事情。
为POCO实体手工生成代理实例
为了允许可以添加或附加的代理实例的生成,你可以使用ObjectContext上的CreateObject工厂方法来生成实体实例:
Category category = context.CreateObject<Category>();
要把这个记住,在生成你想要用于实体框架的实例时,要使用CreateObject。
用“变动跟踪代理(Change Tracking Proxies)”来提供更有效的变动跟踪
到目前为止,我们讨论过的标准POCO实体都依赖于基于快照(snapshot)的变动跟踪,即,实体框架会保管实体变动之前的值和关系的快照,这样,在保存(Save)时,可以与当前的值做比较。但这个比较的花销是相当大的,如果跟基于EntityObject的实体的变动跟踪的方式相比的话。
还有另外一种类型的代理,它允许你在使用POCO实体时得到比较好的变动跟踪性能。
如果你熟悉IPOCO接口,你知道IEntityWithChangeTracker是要求你在类中实现、来向实体框架提供变动通知的接口之一。
变动跟踪代理从你的POCO实体类继承而来,在运行时给你提供这个功能,而不要求你自己实现IPOCO接口。
从许多方面讲,用这种方式的话,你是鱼与熊掌都兼得了:你得到了POCO类的透明持久性,在变动跟踪方面你也得到了EntityObject / IPOCO 的性能。
为了得到变动跟踪代理,基本的规则是,你的类必须是公开的,非抽象的或者非密封的(non-sealed)。你的类对所有要持久的属性都必须实现公开的virtual getters/setters。最后,你必须将基于集合的关系导航属性严格声明为ICollection<T>。它们不能是具体的实现或者继承自ICollection<T>的另外的接口(与延迟装载代理有所不同)。
这里是我的Product POCO类的例子,它将在运行时给我提供更有效的基于代理的变动跟踪:
public class Product
{
public virtual int ProductID { get; set; }
public virtual string ProductName { get; set; }
public virtual int SupplierID { get; set; }
public virtual string QuantityPerUnit { get; set; }
public virtual decimal UnitPrice { get; set; }
public virtual InventoryDetail InventoryDetail { get; set; }
public virtual bool Discontinued { get; set; }
public virtual Category Category { get; set; }
}
再说一遍,要记住,如果你要将实体加到或附加到上下文的话,你必须使用CreateObject来生成代理实例。但不依赖代理的纯粹的POCO实体和基于代理的实体可以共处。你只有在涉及基于代理的实体时才需使用CreateObject。
如果我想在同个POCO类型中同时启用延迟装载和更好的变动跟踪,该怎么办?
这两个东西不是互相排斥的,你不必在延迟装载代理和变动跟踪代理间做选择。如果你想要延迟装载,以及有效的变动跟踪,你只要按照变动跟踪代理的规则办,以及启用延迟装载选项。变动跟踪代理会给你提供延迟装载,如果延迟装载选项是启用了的话。
显式装载
这个延迟装载的功能确实很棒,但你们中很多人大概想要完全控制你是如何装载相关实体的吧。你甚至会选择走纯粹的POCO之路,而不诉诸于任何自动代理生成能给你提供的功能。
这是完全可以接受的,(在很多情形下也许是更好的方法),你可以使用显式关系装载,对你是如何在数据库中查询数据的做完全的控制。
在POCO中做显式装载,有2个选项:
一个是使用 ObjectContext.LoadProperty ,设置你想要装载的导航属性的名称:
context.LoadProperty(beveragesCategory, "Products");
这是可行的,但你可以看出来,这并不类型安全(type safe)。如果我没有正确的导航属性的名称的话,我会得到一个运行时异常。
你们中的一些人大概更喜欢这个:
context.LoadProperty(beveragesCategory, c => c.Products);
我可以使用lambda表达式来指定我想要显式装载的属性,这提供了更好的类型安全。
上面是我计划在这第二个贴子里讨论的所有的内容了。但以后还会有更多内容,在这个系列的最后一篇里,我们将讨论在处理纯POCO(非代理化的)实例以及在你的对象图和对象状态管理器(Object State Manager)间保持一致时需要知道的几件事情。我们也会讨论你也许想要知道的SaveChanges方法的多个变种。
与此同时,请看一下我更新过的样例代码,其中包括了我们在本贴里讨论过的一些东西。
Faisal Mohamood
Entity Framework的Program Manager
推荐文章