2017-08-01 110 views
0

這個問題不是關於entityframework本身的異步問題,如討論的hereEF-Core方法調用LINQ性能

在方法CalculateSomething中,您可以看到兩個LINQ調用。 第一次LINQ調用(初始化爲result)的性能絕對沒問題。

但是,第二次LINQ-Call(初始化resultWithDate)的性能比第一次慢。

第一個需要2秒,第二個需要15-20秒。

dataBase是我的DbContext類。 Iam使用實體框架核心。

private async Task<long> CalculateSomething(string numberOne, MyStatus status) 
    { 
    var result = await this.dataBase.Something.CountAsync(item => item.NumberOne== numberOne && item.Status == (short)status); 
    var resultWithDate = await this.dataBase.Something.CountAsync(item => item.NumberOne== numberOne && item.Status == (short)status && !this.IsOlderThan30Days(item.Date)); 

    return result; 
    } 

    private bool IsOlderThan30Days(DateTime? itemDate) 
    { 
    bool result = true; 

    if (itemDate.HasValue) 
    { 
     if ((DateTime.Now - itemDate.Value).TotalDays <= 30) 
     { 
      result = false; 
     } 
    } 

    return result; 
    } 

的問題不是方法調用IsOlderThan30Days,這個問題是關於CountAsync。我知道這是因爲我有這樣的事情:

private async Task<long> CalculateAmountOfOrders(string numberOne, MyStatus status) 
    { 
    var result = this.dataBase.Something.Where(item => item.NumberOne == numberOne && item.Status == (short)status); 
    var resultWithDate = this.dataBase.Something.Where(item => item.NumberOne == numberOne && item.Status == (short)status && !this.IsOlderThan30Days(item.Date)); 

    var resultCount = await result.CountAsync(); 
    var resultWithDateCount = await resultWithDate.CountAsync(); 

    return resultCount; 
    } 

而且性能損失出現在兩個CountAsync()電話。 CountAsyncresultWithDateCount花了15秒,而CountAsyncresultCount只花了2秒。初始化resultresultWithDate同樣快。

我做錯了什麼?

謝謝

+0

您是否可以在每次操作後嘗試處理上下文並重做測試?就像'使用(var db = new Context()){countOperation(); }' 其他:你確定你的查詢是通過EF執行的嗎?因爲我不認爲EF可以通過LinqToEntities來處理'IsOlderThan30Days' ... – Atlasmaybe

+1

'初始化結果和resultWithDate同樣快。'這是因爲在你的後面的代碼中,初始化結果和resultWithDate實際上並沒有做初始化非常。請參閱https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution。 – mjwills

+1

由於您在seocond查詢中使用了方法('IsOlderThan30Days()'),因此它在內存中執行(因爲該私有方法無法轉換爲/在SQL中執行)。因此,EF必須在使用where表達式進行篩選之前加載所有項目,這很可能是導致性能差異的原因。 (你可以檢查'result'和'resultWithDate'的類型嗎?如果我是正確的,那麼第一個查詢是'IQueryable',而另一個是'IEnumarable') – bassfader

回答

3

試試這個:

var date = DateTime.Now.AddDays(-30); 
var result = await this.dataBase.Something.CountAsync(item => item.NumberOne == numberOne && item.Status == (short)status); 
var resultWithDate = itemDate.HasValue ? await this.dataBase.Orders.CountAsync(item => item.NumberOne == numberOne && item.Status == (short)status && 
     itemDate.Value < date) : 0; 

或者:

var date = DateTime.Now.AddDays(-30); 
var result = await this.dataBase.Something.CountAsync(item => item.NumberOne == numberOne && item.Status == (short)status); 
var resultWithDate = await this.dataBase.Orders.CountAsync(item => item.NumberOne == numberOne && item.Status == (short)status && 
     itemDate < date); 

的關鍵是努力做30天的日期計算的LINQ之外

+0

完美答案!爲我工作!非常感謝你的努力! –

+0

強調應該更多地使用內聯表達式來應用日期過濾器,而不是使用私有方法。在表達式中引入方法調用的那一刻,EF Core就不能再評估方法調用的內部,因此它必須從數據庫加載數據以在每個結果上運行該方法。 – Smit

+0

也可以內聯日期計算,因爲它也會被轉換爲SqlServer。如果您定義了一個變量,那麼EF將在內存中對其進行評估,並在翻譯之前將其粘貼到表達式樹中。 – Smit

1

確定這裏是@mjwills靈感代碼:

private async Task<long> CalculateSomething(string numberOne, MyStatus status) 
{ 
    var date = DateTime.Now.AddDays(-30); 

    var result = await this.dataBase.Something.CountAsync(item => 
    item.NumberOne == numberOne && item.Status == (short)status); 
    var resultWithDate = await this.dataBase 
    .Something 
    .CountAsync(item => item.NumberOne == numberOne && item.Status == (short)status && (!item.Date.HasValue || item.Date.Value <= date)); 

    return result; 
} 
+1

pfff當然我是多麼愚蠢......我看到了另一個答案,並認爲他們是從中得到的東西。我會試着打開我的工作室,以便能夠做出更可靠的答案。 – JBO

+0

@dasblinkenlight,我用一段新的代碼編輯了答案。我希望這會比第一個更有幫助。 – JBO

+0

工作也是如此!謝謝! –

1

的問題是,IsOlderThan30Days部隊將數據加載到內存中。通過在數據庫中進行計算,您應該能夠獲得一些性能:

var now = DateTime.Now; 
var resultWithDate = await this.dataBase 
    .Something 
    .CountAsync(item => 
     item.NumberOne== numberOne 
    && item.Status == (short)status 
    && (item.Date != null && EntityFunctions.DiffDays(item.Date, now) <= 30) 
    );