Entity Framework 更新性能

Entity Framework Update performance

场景:Data Transfer\Link旧数据库中的记录,根据我创建的新模式将实体更新到新数据库中。

您对改进这段代码有什么建议吗?我真的需要能够实现卓越的性能。

提前致谢!

private async Task LinkCategoriesToBillingTransactions()
{
    var legacyTransactions = _legacyDb
            .TbTransactions
            .AsNoTracking()
            .Where(x => x.Id >= 1150534);

    foreach (var legacyTransaction in legacyTransactions) 
    {
        var legacyTransactionTask = _legacyDb.TbCategories
                        .AsNoTracking()
                        .SingleOrDefaultAsync(x =>
                                x.Id == legacyTransaction.CategoryId);
        var transactionTask = _cmDb.BillingTransactions
                        .SingleOrDefaultAsync(x => 
                                x.UniqueId == legacyTransaction.Guid);

        await Task.WhenAll(legacyTransactionTask, transactionTask);

        var legacyCategory = legacyTransactionTask.Result;
        var transaction = transactionTask.Result;

        if (legacyCategory == null || transaction == null)
            continue;

        var transactionCategory = await _cmDb.Categories
                         .SingleOrDefaultAsync(x => 
                                 x.UniqueId == legacyCategory.Guid);

        transaction.Category = transactionCategory;

        await _cmDb.SaveChangesAsync();
    }
}

要提高性能,您需要减少数据库往返次数。

Tb 类别 && 类别

你有多少个类别?可能最多 5000 个类别!

所以将它们全部加载到内存中并从中创建一个字典。

您将只进行一次往返,而不是为每个遗留事务执行一次数据库往返

var legacyCategoriesDict = _legacyDb.TbCategories.AsNoTracking().ToDictionary(x => x.Id);
var transactionCategorisDict = cmDb.Categories.ToDictionary(x => x.UniqueId);

计费交易

对于 BillingTransactions,我们不能这样做,因为您可能有数十万笔交易。

如果你能全部加载,那很好!就像我们对类别所做的一样,否则会批量加载它们。

像这样

legacyTransactionsList = legacyTransactions.ToList();
var legacyTransactionsCount = legacyTransactionsList.Count();

List<BillingTransaction> billingTransactions = new List<BillingTransaction>();
int batchSize = 2000; // Be careful, SQL is limited to 2100 parameters
for(int i = 0; i < 1 + (legacyTransactionsCount % batchSize); i++)
{
    var listGuid = legacyTransactionsList.Skip(i * batchSize).Take(batchSize).Select(x => x.Guid);
    var billingTransactionsToAdd = cmDb.BillingTransactions.Where(x => listGuid.Contains(x.UniqueId));
    billingTransactions.AddRange(billingTransactionsToAdd);
}

billingTransactionsDict = billingTransactions.ToDictionary(x => x.UniqueId);

您不会在一次往返中获得所有交易,但会大大提高性能。

保存更改

SaveChanges 方法为每个要保存的实体执行一次数据库往返,这是 INSANELY 慢。

免责声明:我是项目的所有者Entity Framework Extensions

这个库不是免费的,但允许您执行所有批量操作,包括应用程序所需的 BulkUpdate:

  • 批量保存更改
  • 批量插入
  • 批量更新
  • 批量删除
  • 批量合并
  • 批量同步

示例:

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});