Entity Framework 4.1 之三 : 貪婪加載和延遲加載
原文名稱:Entity Framework 4.1: Deep Fetch vs Lazy Load (3)
原文地址:http://vincentlauzon.wordpress.com/2011/04/11/entity-framework-4-1-deep-fetch-vs-lazy-load-3/
這篇文章將討論查詢結(jié)果的加載控制。
EF4.1 允許控制對(duì)象之間的關(guān)系,當(dāng)我們進(jìn)行查詢的時(shí)候,哪些關(guān)系的數(shù)據(jù)將會(huì)被加載到內(nèi)存呢?所有相關(guān)的對(duì)象都需要嗎?在一些場(chǎng)合可能有意義,例如,當(dāng)查詢的實(shí)體僅僅擁有一個(gè)相關(guān)的子實(shí)體,但是,多數(shù)情況下,你可能只需要加載部分?jǐn)?shù)據(jù),或者你喜歡的話,加載更多的數(shù)據(jù)。
默認(rèn)情況下, EF4.1 僅僅加載查詢中涉及的實(shí)體,但是它支持兩種特性來(lái)幫助你控制加載:
- 貪婪加載
- 延遲加載
貪婪加載
對(duì)于下面的查詢
using (var context = new MyDomainContext())
{
var orders = from o in context.Orders.Include("OrderDetails")
where o.CustomerName == "Mac"
select o;
這里我指定加載某些訂單,就是客戶名為 Mac 的客戶的訂單,而且希望相關(guān)的訂單明細(xì)也一起加載。
你可以這樣查看實(shí)際執(zhí)行的 SQL 查詢
Console.WriteLine(orders.ToString());
實(shí)際的 SQL 如下所示:
SELECT
[Project1].[OrderID] AS [OrderID],
[Project1].[OrderTitle] AS [OrderTitle],
[Project1].[CustomerName] AS [CustomerName],
[Project1].[TransactionDate] AS [TransactionDate],
[Project1].[C1] AS [C1],
[Project1].[OrderDetailID] AS [OrderDetailID],
[Project1].[OrderID1] AS [OrderID1],
[Project1].[Cost] AS [Cost],
[Project1].[ItemName] AS [ItemName]
FROM ( SELECT
[Extent1].[OrderID] AS [OrderID],
[Extent1].[OrderTitle] AS [OrderTitle],
[Extent1].[CustomerName] AS [CustomerName],
[Extent1].[TransactionDate] AS [TransactionDate],
[Extent2].[OrderDetailID] AS [OrderDetailID],
[Extent2].[OrderID] AS [OrderID1],
[Extent2].[Cost] AS [Cost],
[Extent2].[ItemName] AS [ItemName],
CASE WHEN ([Extent2].[OrderDetailID] IS NULL) THEN CAST(NULL AS int) ELS
E 1 END AS [C1]
FROM [dbo].[Orders] AS [Extent1]
LEFT OUTER JOIN [dbo].[OrderDetails] AS [Extent2] ON [Extent1].[OrderID]
= [Extent2].[OrderID]
WHERE N'Mac' = [Extent1].[CustomerName]
) AS [Project1]
ORDER BY [Project1].[OrderID] ASC, [Project1].[C1] ASC
EF4.1 生成的 SQL 不是特別易讀,但是這個(gè)查詢你應(yīng)該能夠看懂,訂單明細(xì)被一起加載了。
這帶來(lái)了一個(gè)關(guān)于貪婪加載的問(wèn)題:查詢效率。如果你執(zhí)行這樣的查詢來(lái)獲取訂單和訂單明細(xì),也可以變成寫出等效的查詢語(yǔ)句,如果喜歡的話,可以更加聰明地寫出返回兩個(gè)查詢結(jié)果的查詢,一個(gè)是訂單,另外一個(gè)是訂單明細(xì)。這應(yīng)該更加有效,因?yàn)槟悴恍枰獮槊恳粋€(gè)訂單明細(xì)重復(fù)訂單的信息。由于一些原因,EF 并不支持。記住這一點(diǎn),因?yàn)檫@很容易使性能變差。
無(wú)論如何,你還可以在查詢中包含更多的子集。
var orders = from o in context.Orders.Include("OrderDetails").Include("Businesses")
where o.CustomerName == "Mac"
select o;
延遲加載
另外一個(gè)特性就是延遲加載,默認(rèn)情況下,延遲加載被支持,如果你希望禁用它,必須顯式聲明,最好的位置是在 DbContext 的構(gòu)造器中。
public MyDomainContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
這樣延遲加載就如你所愿了。當(dāng)查詢一個(gè)實(shí)體集的時(shí)候,相關(guān)的子實(shí)體也一并加載。
當(dāng) EF 訪問(wèn)實(shí)體的子實(shí)體的時(shí)候是如何工作的呢?你的集合是 POCO 的集合,所以,在訪問(wèn)的時(shí)候沒(méi)有事件發(fā)生,EF 通過(guò)從你定義的實(shí)體派生一個(gè)動(dòng)態(tài)的對(duì)象,然后覆蓋你的子實(shí)體集合訪問(wèn)屬性來(lái)實(shí)現(xiàn)。這就是為什么需要標(biāo)記你的子實(shí)體集合屬性為 virtual 的原因。
public class Order
{
public int OrderID { get; set; }
public string OrderTitle { get; set; }
public string CustomerName { get; set; }
public DateTime TransactionDate { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
public virtual List<Business> Businesses { get; set; }
}
總結(jié)一下兩種加載方式的特點(diǎn)
貪婪加載:
- 減少數(shù)據(jù)訪問(wèn)的延遲,在一次數(shù)據(jù)庫(kù)的訪問(wèn)中返回所有的數(shù)據(jù)。
- 你需要知道你將作什么,并且顯式聲明
延遲加載:
- 非常寬容,因?yàn)橹辉谛枰臅r(shí)候加載數(shù)據(jù),不需要預(yù)先計(jì)劃
- 可能因?yàn)閿?shù)據(jù)訪問(wèn)的延遲而降低性能,考慮到每訪問(wèn)父實(shí)體的子實(shí)體時(shí),就需要訪問(wèn)數(shù)據(jù)庫(kù)。
現(xiàn)在,什么時(shí)候我們應(yīng)該使用哪種機(jī)制?我的建議是:除非需要循環(huán)中加載數(shù)據(jù),我使用延遲加載。這樣的話,可能會(huì)造成2-3 次服務(wù)器的查詢,但是仍然是可以接受的,特別是考慮到貪婪加載的效率問(wèn)題。
posted on 2011-05-07 10:20 冠軍 閱讀(28055) 評(píng)論(32) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)