2012-02-02 93 views
4

我們使用實體框架4.2和模型優先方法以及DbContext代碼生成。實體框架中的異步查詢和延遲加載

比方說,我們有以下的實體框架數據模型:

class Person 
{ 
    public string Name { get; set; } 
    public Address Address { get; set; } 
} 

class Address 
{ 
    public string City; { get; set; } 
} 

的情況是以下幾點:

  1. 的ViewModel想從數據庫
  2. 任務(異步加載一些數據操作)是爲加載數據而創建的。這個 是因爲我們不希望UI在數據加載時凍結。
  3. 任務(它在一個單獨的線程執行)從數據庫中創建新 數據庫上下文和負荷數據(例如Person對象)
  4. 任務的完成和數據庫上下文被破壞。
  5. 主線程會收到關於任務完成的通知。主線程可以 現在訪問加載的Person對象。
  6. 視圖嘗試通過數據綁定
  7. 視圖以顯示人的姓名和地址在文本框中訪問Person.Name(這裏沒有問題)
  8. 的視圖訪問Person.Address.City - > OOPS!數據庫上下文已經被處置(因爲加載是在單獨的線程中完成的),並且由於惰性加載Person.Address不可訪問!

在第3階段,人被加載方式如下:

using (DatabaseContext context = new DatabaseContext()) 
{ 
    Person person = from p in context.Persons.FirstOrDefault(); 
    return person; 
} 

好吧,我知道(在理論上)我可能會迫使地址對象的裝載方式有兩種: 1)使用DbSet.Include,如:

context.Persons.Include("Address").FirstOrDefault(); 

2)訪問Person.Address而數據庫方面仍然活着,因爲這將迫使地址加載

Person person = context.Persons.FirstOrDefault(); 
Address address = person.Address; 
return person; 

當然第一個是優選的解決方案,因爲它不是那麼難看作爲第二個(只是訪問屬性以強制數據的裝載和丟棄然後結果是難看)。此外,如果是收集(例如人員列表),我將不得不循環收集並分別訪問每個人的地址。第一種解決方案的問題是隻有DbSet具有包含方法,而查詢返回的所有其他集合都沒有。所以,讓我們說我有以下的數據庫結構

class Resource {} 
class Person : Resource { public Address Address { get; set; } } 
class Appointment { public IList<Resource> Resources { get; set; } } 

,我想加載所有的具體任命和包括在每一個資源,它是人的地址,我遇到了麻煩(或至少我想不出辦法來寫查詢它)。這是因爲context.Appointments.Resources不是DbSet類型的,而是沒有Include方法的ICollection。 (好吧,也許在這種情況下,我可以以context.Persons而不是上下文的方式編寫查詢。約會使我可以使用包括但也有許多場景中這是不可能的)


所以基本上,問題是

  1. 這是一個正確的方式做異步訪問數據庫中第一個地方?
  2. 如何解決延遲加載問題?關閉延遲加載不是解決方案(除非它只能針對特定實體完成)

回答

2

您可以在啓動應用程序或測試時設置包含策略的實現。

首先定義一個擴展方法:包括(無爲而爲默認值)定義實際包括機制

/// <summary> 
/// Extension methods specifically for include since this is essential for DomainContext but not part of IQueryable by default 
/// </summary> 
public static class QueryableIncludeExtensions 
{ 
    public static IIncluder Includer = new NullIncluder(); 

    public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) 
     where T : class 
    { 
     return Includer.Include(source, path); 
    } 

    public interface IIncluder 
    { 
     IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class; 
    } 

    internal class NullIncluder : IIncluder 
    { 
     public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) 
      where T : class 
     { 
      return source; 
     } 
    } 
} 

然後創建一個EF具體包入的實現,如:

internal class DbIncluder : QueryableIncludeExtensions.IIncluder 
{ 
    public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) 
     where T : class 
    { 
     return DbExtensions.Include(source, path); 
    } 
} 

最後,鉤DbIncluder執行在項目中你需要它,在我的情況下,我這樣做:

public class DomainContext : DbContext, IDomainContext 
{ 
    static DomainContext() 
    { 
     // register the DbIncluder for making sure the right call to include is made (standard is null) 
     QueryableIncludeExtensions.Includer = new DbIncluder(); 
    } 

現在IQueryable總是有擴展方法:Include available。如果需要,可以將其擴展爲IEnumerable。實際的實現是通過QueryableIncludeExtensions.Cluder簡單設置的