为什么DBSet.Find越来越慢?

时间:2017-08-24 13:10:06

标签: c# mysql performance entity-framework

我在桌面应用程序中使用实体框架访问mysql数据库。数据库安装在同一个系统上,我是现在唯一使用它的人。

我有一个方法可以检查来自某个集合docs的对象是否已经存在于数据库中,如果不是,它们将被添加到数据库中,最后是DBContext上的save方法将被执行。该方法在我的程序中循环执行多次。

我注意到,每次执行该方法时,查找速度都会变得越来越慢,即使要查找的对象数量相当稳定(每次大约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();
}

因此,在数据库中找不到该文档的情况非常罕见,所以这里基本上唯一的数据库活动是Find方法。
 timetoFind 通常随着方法的每次执行而增加。在可能20-30次循环之后,可能以0.5-2s开始达到120s。

1 个答案:

答案 0 :(得分: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之前不需要检测任何更改,则可以使用{{{}来关闭跟踪。 3}}。这大大提高了查询和添加项目的速度。缺点是在使用相关对象时必须使用ID而不是对象。