簡體   English   中英

實體框架6 DbSet.Find效率

[英]Entity Framework 6 DbSet.Find efficiency

我有兩種通過其ID查找實體的方法。 第一個使用IQueryable對象使用Lambda表達式按其ID查找對象,另一個使用內置的Entity Framework DbSet.Find()方法。 我在Visual Studio中編寫了幾個單元測試,以為這兩種方法創建速度基准,以確定哪種方法更好。

有趣的是,我編寫的方法比“查找”中構建的實體框架獲得更好的結果。 有人知道為什么嗎?

這是Entity Framework方法的代碼:

public virtual T Find<T>(int id)
{
    return dbContext.Set<T>().Find(id);
}

這是我的方法的代碼:

public virtual T FindById<T>(int id)
{
    return dbContext.Set<T>().Where(x => x.IsActive).AsQueryable().FirstOrDefault(x => x.Id == id);
}

這是從數據庫中選擇一些記錄所需的時間:

基准測試

這是我的測試課:

[TestClass]
public class EntityBenchmarks
{
    EdiDataStore target;

    [TestInitialize]
    public void Start()
    {
        target = new EdiDataStore();
    }

    [TestCleanup]
    public void Cleanup()
    {
        target.Dispose();
    }

    //this method is only here because i want to make sure that the 
    //database context is loaded into memory so that we can compare 
    //Find and FindById on an even scale.  Without this method, the first 
    //time benchmark that runs is hit with the overhead of loading the model.
    [TestMethod]
    public void Control()
    {
        var entities = target.GetAgencies();
    }

    [TestMethod]
    public void FindBenchmark()
    {
        bool isSuccess = true;

        for (int i = 9177; i <= 9187; i++)
        {
            var entity = target.Find<Spot>(i);
            isSuccess = isSuccess && entity != null;
        }

        Assert.IsTrue(isSuccess);
    }

    [TestMethod]
    public void FindByIdBenchmark()
    {
        bool isSuccess = true;

        for (int i = 9177; i <= 9187; i++)
        {
            var entity = target.FindById<Spot>(i);
            isSuccess = isSuccess && entity != null;
        }

        Assert.IsTrue(isSuccess);
    }
}

在不知道如何設置測試的情況下,甚至無法嘗試回答問題。 難道是FindBenchmark首先運行並且時間包括引導EF的成本? EF對與實際查詢無關但與延遲初始化無關的第一個查詢進行了一些繁重的工作,因此您不能真正將第一個查詢與后續查詢進行比較。 另一方面,“ Find ”首先在EF跟蹤的實體中查找實體,而FindById始終會進入數據庫-如果您使用相同的上下文並切換順序,則結果可能會完全不同,因為FindById會將實體和Find將不會訪問數據庫。

性能測試有幾處錯誤。

  1. Pawel的回答是正確的,即EF首次運行時,會發生大量開銷。 第一個EF查詢花費的時間比其他查詢長得多。

  2. 您沒有測試相同的查詢。 查找“使用給定的主鍵值查找實體”( https://msdn.microsoft.com/zh-cn/library/gg696418(v=vs.113).aspx )。 我假設您的IsActive標志不是主鍵的一部分,因此上述方法將產生不同的SQL語句。 您無法使用不同的SQL語句比較兩種方法的性能。

    例如:dbContext.Set()。Find(id)會產生類似以下內容:

    選擇*從Foo WHERE FooID = 123

    另一方面,dbContext.Set()。Where(f => f.IsActive).FirstOrDefault(f => f.FooID == id)會產生類似以下內容:

    SELECT * FROM Foo,FooID = 123 AND IsActive = 1

    根據SQL使用什么索引以及SQL Server決定建立哪種執行計划,這本身可以產生截然不同的結果。 如果要比較蘋果與蘋果,則應進行比較:

    dbContext.Set()求(123)。

    dbContext.Set()。Single(f => f.FooID == 123)

  3. 您的FindByID正在執行無關的代碼。 您正在執行Where(...),然后對Where的結果調用AsQueryable(),然后調用FirstOrDefault()。 IQueryable(T).Where()擴展方法已經返回IQueryable(T)。 此后無需調用AsQueryable()。

如果要顯式測試檢索相同數據的不同方法的性能,則需要確保這些方法在做的事情完全相同。 最好的保證方法是運行SQL事件探查器,並確保所生成的基礎SQL是相同的。 否則,您正在測試的是SQL Server是否可以生成兩個不同的數據庫查詢,而這些查詢所花費的時間比彼此執行的時間長或短,這是沒有意義的。

另外,請勿在查詢中調用無關的擴展方法。 在這種情況下,AsQueryable是無害的,但是調用錯誤的擴展方法也會使結果集具體化,將每個實體都帶回到內存中,並對內存中的集合運行另一個查詢。 這也會對性能產生巨大影響。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM