使用Entity Framework時有哪些好的設計實踐

asp.net entity-framework visual-studio-2008

這主要適用於不通過soa訪問數據的asp.net應用程序。這意味著您可以訪問從框架加載的對象,而不是轉移對象,儘管某些建議仍然適用。

這是一個社區帖子,所以請在您認為合適時添加。

適用於 :Visual Studio 2008 sp1附帶的Entity Framework 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作為狗,其中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()函數,它關心狗的不同關係。一個關心業主,另一個關心他的最喜歡的食物,另一個關於他的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();
}

include只使用傳遞的字符串。很容易。請注意,可以使用IncludeMany(字符串)改進Include(字符串)函數(僅接受單個路徑),該函數將允許您將一串逗號分隔的關聯傳遞給load。在此功能的擴展部分中進一步查看。

但是,如果我們嘗試使用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" );

因為,如上所述,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()只需要一個路徑。該怎麼辦?您可以決定永遠不會需要超過20個,包括,並將結構中的每個分隔的字符串傳遞給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(或Include,Where,OrderBy ...)時,你使緩存的編譯查詢失效,因為它現在是一個全新的!因此,需要重新表達表達式樹,並再次獲得性能。

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

一個使用的例子是:你可能想要一個頁面來查詢哪兩隻狗有相同的喜歡的食物,這對於BusinessLayer功能來說有點狹窄,所以你把它放在你的頁面中並確切地知道包含的類型是什麼需要。

將超過3個參數傳遞給CompiledQuery

Func限制為5個參數,其中最後一個是返回類型,第一個是模型中的Entities對象。所以這給你留下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()時,將根據編譯的查詢執行查詢,然後,對內存中的對象執行OrderBy。它可能會慢一點,但我甚至不確定。可以肯定的是,您不必擔心錯誤處理ObjectQuery並使編譯的查詢計劃無效。

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

性能

編譯查詢對性能有何影響?它實際上可能相當大。根據經驗,編譯和緩存查詢以便重用至少需要兩倍的時間來簡單地執行它而不進行緩存。對於復雜的查詢(read 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 /字符串連接。 - 當您使用一小部分數據時或者必須首先獲取數據上的所有數據時使用內存中過濾(如果所有數據的性能都很好,那麼內存中的過濾將不會導致任何時間花在數據庫中)。

單身人士訪問

處理所有頁面中的上下文和實體的最佳方法是使用單例模式:

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的Dog擁有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是可能的,但它要求您在更新/插入/刪除時要格外小心。問題是,如果你混合,那麼你可能會冒險讓框架嘗試將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個不同的對象,一個跟踪,一個不跟踪。在更新/插入中使用分離的對象將強制使用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屬性。

在嘗試優化性能時要記住一些事項。

沒有懶加載,我該怎麼辦?

這可以被視為偽裝的祝福。當然手動加載所有東西都很煩人。但是,它會減少對數據庫的調用次數,並迫使您考慮何時應該加載數據。您在一個數據庫中加載的次數越多越好。這總是正確的,但現在通過EF的這個“功能”強制執行。

當然,你可以調用if(!ObjectReference.IsLoaded)ObjectReference.Load();如果你願意,但更好的做法是強制框架加載你知道一次性需要的對象。這是關於參數化包含的討論開始有意義的地方。

讓我們說你有狗對象

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

這是您一直使用的功能類型。它從各個地方被調用,一旦你擁有了Dog對象,你將在不同的函數中做很多不同的事情。首先,它應該是預編譯的,因為你會經常調用它。其次,每個不同的頁面都希望能夠訪問Dog數據的不同子集。有些人會想要主人,有些人會想要收藏玩家等。

當然,您可以隨時為每個需要的引用調用Load()。但是每次都會產生對數據庫的調用。餿主意。因此,每個頁面首次請求Dog對象時都會詢問它想要查看的數據:

    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 " +

熱門答案

請不要使用上述所有信息,例如“單身人士訪問”。絕對100%不應該存儲此上下文以供重用,因為它不是線程安全的。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因