2017-08-24 120 views
3

我在桌面應用程序中使用實體框架訪問mysql數據庫。數據庫安裝在同一個系統上,而我現在是唯一使用它的系統。爲什麼DBSet.Find越來越慢?

我有一種方法,檢查某個集合docs中的對象是否已經在數據庫中,如果它們不是它們將被添加到數據庫中,並且最後會執行DBContext上的保存方法。該方法在我的程序中循環執行多次。

我注意到,即使要查找的對象數量相當恆定(每次大約500次),每次執行該方法時查找速度都會變得越來越慢。可能是什麼原因?

代碼看起來或多或少是這樣的:

TimeSpan timeToFind = new TimeSpan(); 

foreach (var docFromResult in docs) 
{ 
    DateTime operationStart = DateTime.Now; 
    var existingDocument = 
     db.VaStDocuments.Find(docFromResult.Id, docFromResult.OwnerId, docFromResult.Year); 
    timeToFind += DateTime.Now - operationStart; 
    if (existingDocument != null && docFromResult.Hash.Equals(existingDocument.Hash)) 
    { 
     continue; 
    } 
    if (existingDocument == null) 
    { 
     db.VaStDocuments.Add(docFromResult); 
    } 
    else if (!docFromResult.Hash.Equals(existingDocument.Hash)) 
    { 
     existingDocument.Hash = docFromResult.Hash; 
     existingDocument.IsNew = true; 
     existingDocument.Text = null; 
    } 
    docsForNotification.Add(docFromResult); 
} 
if (docsForNotification.Any()) 
{ 
    db.SaveChangesDisplayValidationErrors(); 
} 

因此,它是該文件將無法在數據庫中找到這樣基本上是唯一的數據庫活動在這裏查找方法非常罕見的情況。
timetoFind通常隨着方法的每次執行而增加。在20-30個循環之後,從0.5-2s開始到120s。

+3

您追蹤的實體列表將會增加,因此您可以嘗試避免:'db.VaStDocuments.AsNoTracking()。查找(...)' – DavidG

+0

@DavidG - 任何副作用? – Greg

+0

除了性能提升之外,此代碼中沒有任何內容。如果你在其他地方使用這些實體,它可能會受到一些影響,但我非常懷疑這一點。 – DavidG

回答

1

您的DbContext會跟蹤您檢索的項目以及您對這些檢索項目所做的更改。

這是爲了能夠添加/更新/刪除這些項目,而無需與每個操作的數據庫進行通信。只有在調用SaveChanges時,纔會將更改與數據庫進行通信。

這使您可以在將這些更改提交到數據庫之前使用新添加或更改的對象。

考慮一對多關係運輸客戶訂單:客戶有零個或多個訂單,每個訂單隻屬於一個客戶。

現在您可以先介紹一位新客戶。之後,你可以介紹訂單,而客戶沒有ID尚未:

using (var dbContext = new MyDbContext(...)) 
{ // Introduce a Customer: 
    Customer customerToAdd = GetCustomerData(); 
    var addedCustomer = dbContext.Customers.Add(customerToAdd); 

    // Introduce an order for this Customer 
    Order addedOrder = dbContext.Orders.Add(new Order() 
    { 
     // addedCustomer has no Id yet, we can't use addedCustomer.Id 
     Order.Customer = addedCustomer; 
     ... 
    }); 
    dbContext.SaveChanges(); 
} 

因爲不停的DbContext跟蹤添加客戶的您能夠使用它保存的更改。

它還使您無需增加客戶至上添加順序爲:

var notAddedCustomer = new Customer() {...} 
var order1= dbContext.Orders.Add(new Order() 
{ 
    // this order belongs to a new customer that has not been added yet: 
    Customer = notAddedCustomer 
} 

的DbContext檢測到客戶尚未添加,並添加它本身。如果您要爲同一個notAddedCustomer創建另一個訂單,那麼dbContext必須能夠檢測到該客戶是在引入第一個訂單期間添加的。另外,如果您刪除了一個客戶,並嘗試給他一個新的訂單,那麼dbContext需要檢測到該客戶已被刪除,並且無法添加新的訂單。同樣,如果您在添加一些訂單後刪除客戶,則dbContext應該檢測到這一點。

這就是爲什麼dbContext需要跟蹤更改。唉,如果你有很多改變的對象,這會減慢使用率。大多數dbContext的用戶只能在相當短的時間內進行相當少量的更改。

如果您在進行SaveChanges之前需要做很多更改,並且確定在執行實際SaveChanges之前不需要檢測它們中的任何一個,則可以使用Queryable.AsNoTracking關閉跟蹤。這大大提高了查詢和添加項目的速度。缺點是在使用相關對象時必須使用Ids而不是對象。

+0

謝謝你的回答。我注意到了兩件事 - 即使我經常打電話給'SaveChanges',如果我不使用AsNoTracking,性能仍然很慢。當我使用AsNoTracking與保存docFromResult,因爲它引用另一個所有者實體。如果我也檢索Owner AsNoTracking,則在保存時會出現錯誤。所有者已經在數據庫中,不需要添加。但也許我應該爲此做一個單獨的問題。 – Greg

+1

SaveChanges不會刪除跟蹤的項目。優點:如果再次請求相同的項目,則不必從數據庫中查詢。如果關閉使用(並因此Dispose dbContext),然後重新創建一個新的DbContext對象 –

+0

,那麼通過在每個循環中重新初始化/配置dbContext,看起來會比使用40-50倍更好。在進行一些同步時掙扎了一下,但仍然比追蹤所有事情少得多。 – Greg