2009-02-13 95 views
74

這主要適用於不通過soa訪問數據的asp.net應用程序。這意味着您可以訪問從框架加載的對象,而不是傳輸對象,儘管一些建議仍然適用。使用實體框架時,什麼是良好的設計實踐

這是社區帖子,所以請根據需要添加。

適用於:Visual Studio 2008 sp1附帶的實體框架1.0。

爲什麼選擇EF?

考慮到這是一個年輕的技術有很多問題(見下文),它可能是一個很難賣你EF項目的行列。然而,這是微軟推動的技術(以Linq2Sql爲代表的EF子集)。另外,你可能不滿意NHibernate或其他解決方案。無論是什麼原因,有人(包括我)與EF一起工作,生活並不壞。讓你想想。

EF和繼承

第一個大的主題是傳承。 EF支持映射繼承的類,這些類以兩種方式保存:每個類的表和表的層次結構。建模非常簡單,該部分沒有編程問題。

(以下內容適用於每個類模型的表,因爲我沒有每個層次結構的表的經驗,無論如何,這是有限的。)真正的問題出現在您嘗試運行包含一個或多個對象的查詢時它們是繼承樹的一部分:生成的sql非常糟糕,需要很長時間才能被EF解析並且需要很長時間才能執行。這是一個真正的表演塞。足夠的EF可能不應該用於繼承或儘可能少。

這是一個很糟糕的例子。我的EF模型有30個類,其中約10個是繼承樹的一部分。在運行查詢以從Base類獲取一個項目時,如Base.Get(id)那樣簡單,生成的SQL超過了50,000個字符。然後,當您嘗試返回一些關聯時,它甚至退化得更多,只要拋出SQL異常就無法一次查詢超過256個表。

好吧,這是不好的,EF的概念是允許你創建你的對象結構沒有(或儘可能少)考慮你的表的實際數據庫實現。這完全失敗了。

那麼,建議?如果可以,避免繼承,性能會好很多。在必要的地方謹慎使用它。在我看來,這使得EF成爲查詢的優化SQL生成工具,但使用它仍然有優勢。以及如何實現類似於繼承的機制。

與接口

第一件事繞過繼承知道,試圖獲得某種繼承與EF會是你無法分配非EF-模擬類的基類。不要嘗試它,它會被建模者覆蓋。那麼該怎麼辦?

您可以使用接口來強制該類實現某些功能。例如,這裏是一個IEntity接口,它允許您定義在設計時不知道實體類型的EF實體之間的關聯。

public enum EntityTypes{ Unknown = -1, Dog = 0, Cat } 
public interface IEntity 
{ 
    int EntityID { get; } 
    string Name { get; } 
    Type EntityType { get; } 
} 
public partial class Dog : IEntity 
{ 
    // implement EntityID and Name which could actually be fields 
    // from your EF model 
    Type EntityType{ get{ return EntityTypes.Dog; } } 
} 

使用此IEntity,然後你可以用不確定協會其他類的工作

// lets take a class that you defined in your model. 
// that class has a mapping to the columns: PetID, PetType 
public partial class Person 
{ 
    public IEntity GetPet() 
    { 
     return IEntityController.Get(PetID,PetType); 
    } 
} 

這使得使用一些擴展功能:

public class IEntityController 
{ 
    static public IEntity Get(int id, EntityTypes type) 
    { 
     switch (type) 
     { 
      case EntityTypes.Dog: return Dog.Get(id); 
      case EntityTypes.Cat: return Cat.Get(id); 
      default: throw new Exception("Invalid EntityType"); 
     } 
    } 
} 

不整齊具有普通繼承,特別是考慮到您必須將PetType存儲在額外的數據庫字段中,但考慮到性能的提升,我不會回頭看。

它也不能模擬一對多,多對多的關係,但通過創造性地使用「聯盟」,它可以發揮作用。最後,它創建了在對象的屬性/函數中加載數據的副作用,您需要小心。使用像GetXYZ()這樣清晰的命名約定有助於這方面的工作。

編譯查詢

實體框架的表現並不如ADO(顯然)或LINQ2SQL直接訪問數據庫一樣好。有辦法改善它,但其中之一是編譯你的查詢。編譯查詢的性能與Linq2Sql類似。

什麼是編譯查詢?它只是一個查詢,告訴框架將分析的樹保存在內存中,以便在下次運行時不需要重新生成。所以下一次運行,您將節省解析樹所花費的時間。不要打賭,因爲這是一個非常昂貴的操作,更復雜的查詢變得更糟。

有兩種編譯查詢的方法:使用EntitySQL創建ObjectQuery並使用CompiledQuery.Compile()函數。 (請注意,通過在頁面中使用EntityDataSource,實際上您將使用EntitySQL中的ObjectQuery,以便進行編譯和緩存)。

如果你不知道EntitySQL是什麼的話,在這裏旁邊。這是一種針對EF編寫查詢的基於字符串的方式。這裏是一個例子:「從Entities.DogSet中選擇值狗作爲狗,其中dog.ID = @ID」。該語法與SQL語法非常相似。您也可以執行相當複雜的對象操作,這很好解釋[這裏] [1]。

好了,所以這裏是如何使用的ObjectQuery <>

 string query = "select value dog " + 
         "from Entities.DogSet as dog " + 
         "where dog.ID = @ID"; 

     ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance)); 
     oQuery.Parameters.Add(new ObjectParameter("ID", id)); 
     oQuery.EnablePlanCaching = true; 
     return oQuery.FirstOrDefault(); 

當您第一次運行此查詢做到這一點,該框架將生成表達式樹,並保持它在內存中。所以下次執行時,您將節省昂貴的一步。在那個例子中,EnablePlanCaching = true,這是不必要的,因爲這是默認選項。

編譯查詢供以後使用的另一種方法是CompiledQuery.Compile方法。這使用委託:

static readonly Func<Entities, int, Dog> query_GetDog = 
     CompiledQuery.Compile<Entities, int, Dog>((ctx, id) => 
      ctx.DogSet.FirstOrDefault(it => it.ID == id)); 

或使用LINQ

static readonly Func<Entities, int, Dog> query_GetDog = 
     CompiledQuery.Compile<Entities, int, Dog>((ctx, id) => 
      (from dog in ctx.DogSet where dog.ID == id select dog).FirstOrDefault()); 

致電查詢:

query_GetDog.Invoke(YourContext, id); 

CompiledQuery的好處是,你的查詢語法在編譯時檢查,EntitySQL不是。不過,也有其他的考慮?

包括

比方說你想擁有的查詢將返回到避免2個調用數據庫的狗主人的數據。容易做到,對吧?

EntitySQL

 string query = "select value dog " + 
         "from Entities.DogSet as dog " + 
         "where dog.ID = @ID"; 
     ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance)).Include("Owner"); 
     oQuery.Parameters.Add(new ObjectParameter("ID", id)); 
     oQuery.EnablePlanCaching = true; 
     return oQuery.FirstOrDefault(); 

CompiledQuery

static readonly Func<Entities, int, Dog> query_GetDog = 
     CompiledQuery.Compile<Entities, int, Dog>((ctx, id) => 
      (from dog in ctx.DogSet.Include("Owner") where dog.ID == id select dog).FirstOrDefault()); 

現在,有你想要的參數化包括?我的意思是你想要有一個單獨的Get()函數,它可以從關注狗不同關係的不同頁面調用。一個關心業主,另一個關於他的FavoriteFood,另一個關於他的FavotireToy等等。基本上,你想告訴查詢加載哪些關聯。

這是很容易與EntitySQL

public Dog Get(int id, string include) 
{ 
     string query = "select value dog " + 
         "from Entities.DogSet as dog " + 
         "where dog.ID = @ID"; 

     ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, EntityContext.Instance)) 
    .IncludeMany(include); 
     oQuery.Parameters.Add(new ObjectParameter("ID", id)); 
     oQuery.EnablePlanCaching = true; 
     return oQuery.FirstOrDefault(); 
} 

的包括做簡單的使用所傳遞的字符串。很簡單。請注意,可以使用IncludeMany(string)改進Include(string)函數(只接受單個路徑),該函數可讓您傳遞一串逗號分隔的關聯以加載。進一步查看該功能的擴展部分。

如果我們嘗試但是與CompiledQuery做到這一點,我們遇到了許多問題:

明顯

static readonly Func<Entities, int, string, Dog> query_GetDog = 
     CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) => 
      (from dog in ctx.DogSet.Include(include) where dog.ID == id select dog).FirstOrDefault()); 

會嗆調用時使用:

query_GetDog.Invoke(YourContext, id, "Owner,FavoriteFood"); 

因爲,作爲mentionned在上面,Include()只希望在字符串中看到單個路徑,在這裏我們給它2:「Owner」和「FavoriteFood」(不要與「Owner.FavoriteFood」混淆!)。

然後,讓我們使用IncludeMany(),這又是一個擴展功能

static readonly Func<Entities, int, string, Dog> query_GetDog = 
     CompiledQuery.Compile<Entities, int, string, Dog>((ctx, id, include) => 
      (from dog in ctx.DogSet.IncludeMany(include) where dog.ID == id select dog).FirstOrDefault()); 

錯了,這一次是因爲EF無法解析IncludeMany,因爲它是不是就是部分功能認識:它是一個擴展。

好吧,所以你想要傳遞任意數量的路徑到你的函數和Includes()只需要一個。該怎麼辦?你可以決定永遠不需要比包含更多的東西,並且將結構中的每個分隔字符串傳遞給CompiledQuery。但現在查詢看起來像這樣:

from dog in ctx.DogSet.Include(include1).Include(include2).Include(include3) 
.Include(include4).Include(include5).Include(include6) 
.[...].Include(include19).Include(include20) where dog.ID == id select dog 

這也很糟糕。好的,那就等一下。我們不能用CompiledQuery返回ObjectQuery嗎?然後設置包含呢?那麼,什麼我還以爲左右爲好:

static readonly Func<Entities, int, ObjectQuery<Dog>> query_GetDog = 
     CompiledQuery.Compile<Entities, int, string, ObjectQuery<Dog>>((ctx, id) => 
      (ObjectQuery<Dog>)(from dog in ctx.DogSet where dog.ID == id select dog)); 
public Dog GetDog(int id, string include) 
{ 
    ObjectQuery<Dog> oQuery = query_GetDog(id); 
    oQuery = oQuery.IncludeMany(include); 
    return oQuery.FirstOrDefault; 
} 

這應該有工作,只是當你調用IncludeMany(或包含,其中,排序依據......),你無效緩存的編譯查詢,因爲它是現在是一個全新的!因此,表達式樹需要重新設置,並且您再次獲得了性能。

那麼解決方案是什麼?您無法將CompiledQueries與參數化的Includes一起使用。改用EntitySQL。這並不意味着CompiledQueries沒有用處。對於始終在相同上下文中調用的本地化查詢來說,這非常棒。理想情況下應該總是使用CompiledQuery,因爲在編譯時檢查語法,但由於限制,這是不可能的。

一個使用的例子是:您可能想要一個頁面來查詢哪兩條狗擁有相同的最喜歡的食物,這對於BusinessLayer函數來說有點狹隘,所以你把它放在你的頁面中,包含類型是必需的。

傳遞多於3個參數的CompiledQuery

func被限制在5個參數,其中最後一個是返回類型和第一個是你的實體從模型對象。所以這給你留下了3個參數。一個pitance,但它可以很容易地改進。

public struct MyParams 
{ 
    public string param1; 
    public int param2; 
    public DateTime param3; 
} 

    static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog = 
     CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) => 
      from dog in ctx.DogSet where dog.Age == myParams.param2 && dog.Name == myParams.param1 and dog.BirthDate > myParams.param3 select dog); 

public List<Dog> GetSomeDogs(int age, string Name, DateTime birthDate) 
{ 
    MyParams myParams = new MyParams(); 
    myParams.param1 = name; 
    myParams.param2 = age; 
    myParams.param3 = birthDate; 
    return query_GetDog(YourContext,myParams).ToList(); 
} 

返回類型(這並不適用於EntitySQL查詢,因爲他們沒有執行的CompiledQuery方法的過程中,同時編譯)

LINQ的工作,你平時不強制詢問,直到最後一刻的執行,如果其他一些功能下游想要更改查詢以某種方式:

static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog = 
     CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) => 
      from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog); 

public IEnumerable<Dog> GetSomeDogs(int age, string name) 
{ 
    return query_GetDog(YourContext,age,name); 
} 
public void DataBindStuff() 
{ 
    IEnumerable<Dog> dogs = GetSomeDogs(4,"Bud"); 
    // but I want the dogs ordered by BirthDate 
    gridView.DataSource = dogs.OrderBy(it => it.BirthDate); 

} 

這是怎麼發生在這裏?通過仍然使用原始ObjectQuery(即實現IEnumerable的Linq語句的實際返回類型),它將使已編譯的查詢失效並強制重新解析。因此,經驗法則是返回對象的List <>。

static readonly Func<Entities, int, string, IEnumerable<Dog>> query_GetDog = 
     CompiledQuery.Compile<Entities, int, string, IEnumerable<Dog>>((ctx, age, name) => 
      from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog); 

public List<Dog> GetSomeDogs(int age, string name) 
{ 
    return query_GetDog(YourContext,age,name).ToList(); //<== change here 
} 
public void DataBindStuff() 
{ 
    List<Dog> dogs = GetSomeDogs(4,"Bud"); 
    // but I want the dogs ordered by BirthDate 
    gridView.DataSource = dogs.OrderBy(it => it.BirthDate); 

} 

當你調用ToList(),查詢被按照已編譯的查詢執行的,然後後來的排序依據是對內存中的對象執行。這可能會慢一點,但我甚至不確定。一件確定的事情是,您不必擔心錯誤處理ObjectQuery並使已編譯的查詢計劃無效。

再一次,這不是一攬子聲明。 ToList()是一種防禦性編程技巧,但如果您有一個不使用ToList()的有效理由,請繼續。有很多情況下,您希望在執行查詢之前優化查詢。

性能

什麼是編譯查詢的性能影響?它實際上可能相當大。一條經驗法則是,編譯和緩存查詢以重用需要至少兩倍於沒有緩存的簡單執行時間。對於複雜的查詢(閱讀inherirante),我已經看到了10秒。

因此,第一次調用預編譯查詢時,您會獲得性能提升。在第一次點擊之後,性能明顯優於相同的非預編譯查詢。幾乎與Linq2Sql相同

當您第一次加載帶有預編譯查詢的頁面時,您將獲得一個命中。它可能會在5-15秒內加載(顯然,多於一個預編譯查詢最終會被調用),而後續的加載時間將少於300毫秒。戲劇性的差異,你決定是否可以讓第一個用戶點擊,或者你想讓腳本調用你的頁面來強制編譯查詢。

該查詢可以被緩存嗎?

{ 
    Dog dog = from dog in YourContext.DogSet where dog.ID == id select dog; 
} 

沒有,即席LINQ查詢不緩存,你會招致生成樹你怎麼稱呼它每一次的成本。

參數化查詢

大多數搜索功能涉及大量參數化查詢。甚至還有可用的庫,可以讓你用lamba表達式構建參數化查詢。問題是你不能使用這些預編譯查詢。周圍的一種方法是映射出在查詢和標誌要使用哪一個所有可能的標準:

public struct MyParams 
{ 
    public string name; 
public bool checkName; 
    public int age; 
public bool checkAge; 
} 

    static readonly Func<Entities, MyParams, IEnumerable<Dog>> query_GetDog = 
     CompiledQuery.Compile<Entities, MyParams, IEnumerable<Dog>>((ctx, myParams) => 
      from dog in ctx.DogSet 
    where (myParams.checkAge == true && dog.Age == myParams.age) 
     && (myParams.checkName == true && dog.Name == myParams.name) 
    select dog); 

protected List<Dog> GetSomeDogs() 
{ 
    MyParams myParams = new MyParams(); 
    myParams.name = "Bud"; 
    myParams.checkName = true; 
    myParams.age = 0; 
    myParams.checkAge = false; 
    return query_GetDog(YourContext,myParams).ToList(); 
} 

這裏的好處是,你得到一個預編譯quert的所有優惠。缺點是,你很可能最終會得到一個很難維護的where子句,你會在預編譯查詢時承擔更大的代價,並且你運行的每個查詢都不如其有效(特別是與加入投入)。

另一種方法是逐個構建一個EntitySQL查詢,就像我們對SQL所做的一樣。

protected List<Dod> GetSomeDogs(string name, int age) 
{ 
string query = "select value dog from Entities.DogSet where 1 = 1 "; 
    if(!String.IsNullOrEmpty(name)) 
     query = query + " and dog.Name == @Name "; 
if(age > 0) 
    query = query + " and dog.Age == @Age "; 

    ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, YourContext); 
    if(!String.IsNullOrEmpty(name)) 
     oQuery.Parameters.Add(new ObjectParameter("Name", name)); 
if(age > 0) 
     oQuery.Parameters.Add(new ObjectParameter("Age", age)); 

return oQuery.ToList(); 
} 

這裏的問題是: - 沒有語法編譯 期間檢查 - 參數的每個不同組合產生不同的查詢,這將需要進行預編譯時,它是第一次運行。在這種情況下,只有4種不同的可能查詢(沒有參數,只包含年齡,僅包含名稱和兩個參數),但您可以看到,通過正常的世界搜索可以找到更多的方法。 - 沒有人喜歡連接字符串!

另一種選擇是查詢數據的大部分子集,然後在內存中將其縮小。如果您正在處理一個明確的數據子集,就像城市中的所有狗一樣,這一點尤其有用。你知道有很多,但你也知道沒有那麼多...所以你的CityDog搜索頁面可以在內存中加載城市的所有狗,這是一個預編譯的查詢,然後細化結果

protected List<Dod> GetSomeDogs(string name, int age, string city) 
{ 
string query = "select value dog from Entities.DogSet where dog.Owner.Address.City == @City "; 
    ObjectQuery<Dog> oQuery = new ObjectQuery<Dog>(query, YourContext); 
    oQuery.Parameters.Add(new ObjectParameter("City", city)); 

List<Dog> dogs = oQuery.ToList(); 

if(!String.IsNullOrEmpty(name)) 
     dogs = dogs.Where(it => it.Name == name); 
if(age > 0) 
     dogs = dogs.Where(it => it.Age == age); 

return dogs; 
} 

當您開始顯示所有數據,然後允許過濾時,它特別有用。

問題: - 如果您不注意子集,可能會導致嚴重的數據傳輸。 - 您只能過濾返回的數據。這意味着如果您不返回Dog.Owner關聯,您將無法過濾Dog.Owner.Name 那麼最佳解決方案是什麼?沒有任何。您需要選擇最適合您和您的問題的解決方案: - 當您不關心預編譯查詢時,使用基於lambda的查詢構建。 - 當對象結構不太複雜時,使用完全定義的預編譯Linq查詢。 - 當結構可能比較複雜時,以及可能產生的不同結果查詢的數量很小(這意味着更少的預編譯命中)時,使用EntitySQL /字符串連接。 - 在處理一小部分數據時或者首先必須首先獲取數據的所有數據時(如果所有數據的性能都正常,則在內存中進行過濾將使用內存中過濾不會在db中花費任何時間)。

辛格爾頓訪問

處理您的上下文和實體翻過所有網頁的最佳方式是使用Singleton模式:

public sealed class YourContext 
{ 
    private const string instanceKey = "On3GoModelKey"; 

    YourContext(){} 

    public static YourEntities Instance 
    { 
     get 
     { 
      HttpContext context = HttpContext.Current; 
      if(context == null) 
       return Nested.instance; 

      if (context.Items[instanceKey] == null) 
      { 
       On3GoEntities entity = new On3GoEntities(); 
       context.Items[instanceKey] = entity; 
      } 
      return (YourEntities)context.Items[instanceKey]; 
     } 
    } 

    class Nested 
    { 
     // Explicit static constructor to tell C# compiler 
     // not to mark type as beforefieldinit 
     static Nested() 
     { 
     } 

     internal static readonly YourEntities instance = new YourEntities(); 
    } 
} 

NoTracking,值得嗎?

執行查詢時,可以告訴框架跟蹤它將返回與否的對象。這是什麼意思?啓用跟蹤功能(默認選項)後,框架將跟蹤對象的情況(它是否已被修改?創建?已刪除?),並且還會將對象鏈接到一起,這是從數據庫進一步查詢時的結果這裏很有意思。

例如,讓我們假設用ID == 2該狗具有所有者哪個ID == 10.

Dog dog = (from dog in YourContext.DogSet where dog.ID == 2 select dog).FirstOrDefault(); 
    //dog.OwnerReference.IsLoaded == false; 
    Person owner = (from o in YourContext.PersonSet where o.ID == 10 select dog).FirstOrDefault(); 
    //dog.OwnerReference.IsLoaded == true; 

如果我們做同樣沒有跟蹤,結果將是不同的。

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>) 
    (from dog in YourContext.DogSet where dog.ID == 2 select dog); 
oDogQuery.MergeOption = MergeOption.NoTracking; 
Dog dog = oDogQuery.FirstOrDefault(); 
    //dog.OwnerReference.IsLoaded == false; 
ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>) 
    (from o in YourContext.PersonSet where o.ID == 10 select o); 
oPersonQuery.MergeOption = MergeOption.NoTracking; 
    Owner owner = oPersonQuery.FirstOrDefault(); 
    //dog.OwnerReference.IsLoaded == false; 

跟蹤是非常有用的,在沒有性能問題,一個完美的世界,這將永遠是。但在這個世界上,就性能而言,它是有代價的。那麼,你應該使用NoTracking來加快速度嗎?這取決於您計劃使用哪些數據。

有沒有可能使用NoTracking查詢的數據在數據庫中進行更新/插入/刪除?如果是這樣,不要使用NoTracking,因爲不會跟蹤關聯,並會導致拋出異常。

在絕對沒有數據庫更新的頁面中,可以使用NoTracking。

混合跟蹤和NoTracking是可能的,但它需要您更新/插入/刪除額外小心。問題是,如果你混合使用,那麼你可能會冒險讓框架嘗試Attach()一個NoTracking對象到追蹤同一對象的另一個副本的上下文。基本上,我的意思是,

Dog dog1 = (from dog in YourContext.DogSet where dog.ID == 2).FirstOrDefault(); 

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>) 
    (from dog in YourContext.DogSet where dog.ID == 2 select dog); 
oDogQuery.MergeOption = MergeOption.NoTracking; 
Dog dog2 = oDogQuery.FirstOrDefault(); 

DOG1和DOG2是2個不同的對象,一個跟蹤和一個沒有。在更新/插入中使用detached對象會強制一個Attach(),它會說「等一下,我已經有一個對象在這裏,數據庫關鍵字相同,失敗」。當Attach()一個對象時,它的所有層次結構都會被連接,從而導致無處不在的問題。要特別小心。

如何更快地進行NoTracking

這取決於查詢是它。有些人比其他人更容易追蹤。我沒有一個快速簡單的規則,但它有幫助。

所以我應該在任何地方使用NoTracking?

不完全是。跟蹤對象有一些優點。第一個是對象被緩存,所以後續對該對象的調用不會觸及數據庫。該緩存僅對YourEntities對象的生命週期有效,如果您使用上述單例代碼,則該對象與頁面生存期相同。一個頁面請求==一個YourEntity對象。因此,對於同一對象的多次調用,每個頁面請求只會加載一次。 (其他緩存機制可以擴展)。

當您使用NoTracking並嘗試多次加載相同的對象時會發生什麼?數據庫每次都會被查詢,所以在那裏會有影響。在單個頁面請求期間,你應該多長時間一次請求同一個對象?當然儘可能少,但它確實發生。

還記得上面有關於自動連接爲您的聯繫嗎?你不必與NoTracking,因此,如果您在多個批次的數據,你會不會有一個鏈接到他們之間:

ObjectQuery<Dog> oDogQuery = (ObjectQuery<Dog>)(from dog in YourContext.DogSet select dog); 
oDogQuery.MergeOption = MergeOption.NoTracking; 
List<Dog> dogs = oDogQuery.ToList(); 

ObjectQuery<Person> oPersonQuery = (ObjectQuery<Person>)(from o in YourContext.PersonSet select o); 
oPersonQuery.MergeOption = MergeOption.NoTracking; 
    List<Person> owners = oPersonQuery.ToList(); 

在這種情況下,沒有狗都會有.Owner屬性設置。

當您嘗試優化性能時,請記住一些事項。

沒有延遲加載,我該怎麼辦?

這可以被看作是變相的祝福。當然,手動加載所有內容都很麻煩。但是,它會減少對db的調用次數,並迫使您考慮何時應加載數據。您可以在一個數據庫中載入的內容越多,呼叫越好。這一直都是如此,但現在EF的這個「特徵」已經實施了。 (!ObjectReference.IsLoaded)ObjectReference.Load();如果(!ObjectReference.IsLoaded)ObjectReference.Load();如果(!ObjectReference.IsLoaded)ObjectReference.Load();則可以調用 。 如果你願意,但更好的做法是強制框架加載你知道你將需要在一個鏡頭中的對象。這是關於參數化包含的討論開始有意義的地方。

比方說你有你的狗對象

public class Dog 
{ 
    public Dog Get(int id) 
    { 
     return YourContext.DogSet.FirstOrDefault(it => it.ID == id); 
    } 
} 

這是你所有的工作時間函數的類型。它會從各地被召喚出來,一旦你擁有了這個Dog對象,你將在不同的功能中爲它做不同的事情。首先,它應該是預編譯的,因爲你會經常這麼叫。其次,每個不同的頁面都希望能夠訪問Dog數據的不同子集。有些人會希望所有者,一些FavoriteToy等。

當然,您可以在任何需要的時候爲每個需要的引用調用Load()。但是每次都會產生一個到數據庫的調用。餿主意。因此,相反,每一頁都將要求它希望看到的數據時,它的狗對象第一個請求:

static public Dog Get(int id) { return GetDog(entity,"");} 
    static public Dog Get(int id, string includePath) 
{ 
     string query = "select value o " + 
      " from YourEntities.DogSet as o " + 
+15

說真的,你有沒有自己的博客或網站,你可以發佈這篇文章? – AnthonyWJones 2009-02-13 19:26:57

+7

@AnthonyWJones,好吧,雖然它不是SO上的平常帖子;如果你聽SO播客,傑夫總是使用這樣的口頭禪,這是程序員分享知識的地方,沒有博客。這不令人反感。根據傑夫的口頭禪,不應該在我看來是封閉的。 – BobbyShaftoe 2009-02-13 19:57:37

回答

1

雖然翔實我認爲它可能是更有利於分享這一切是如何融入一個完整的解決方案架構。示例 - 得到一個解決方案,顯示您在哪裏使用EF繼承和您的替代方案,以顯示它們的性能差異。

3

請不要使用上述所有信息,如「單身訪問」。你絕對100%不應該存儲這個上下文重用,因爲它不是線程安全的。