Entity Framework 批量插入 unreal 慢

Entity Framework bulk insert unreal slow

我正在使用 EF 6。我试图插入大约 200.000 个实体,同时在每 100 个实体之后保存对数据库的更改。

问题是保存 50.000 个实体花了 11 个小时,而且仍然落后 运行。我是 运行 WebJobs,工作发布在与主网站相同的 azure webapp 上。问题是因为那个和 WebJob 没有足够的资源,还是在 100 个实体后保存,还是方法?

方法

public void SaveLeadsForBuyer(ISenderModel model)
{
    var rowCounter = 0;

    foreach (var deliveryRecord in model.Customers.Select(customerModel => new DeliveryRecord()
    {
        BuyerId = model.Buyer.Id,
        AspNetUserId = customerModel.Id,
        DeliveryType = model.Buyer.DeliveryType,
        CreatedOn = DateTime.UtcNow
    }))
    {
        ++rowCounter;

        _unit.Repository<DeliveryRecord>().Insert(deliveryRecord);

        _unit.SaveChangesPartially(rowCounter, 100);
    }

    _unit.SaveChanges();
}

帮手

public static class UnitOfWorkHelper
{
    /// <summary>
    /// Helper method triggers SaveChanges() after amount of rows provided through "amount" parameter in method
    /// </summary>
    /// <param name="unit">UnitOfWork object</param>
    /// <param name="count">Current amount of rows</param>
    /// <param name="saveCount">Amount when to save changes to database</param>
    public static void SaveChangesPartially(this IUnitOfWorkAsync unit, int count, int saveCount)
    {
        if (count % saveCount == 0)
        {
            unit.SaveChanges();
        }
    }
}

它很慢,因为 Entity Framework 对每条记录执行数据库往返。因此,如果您保存 200,000 个实体,那么将执行 200,000 次数据库往返,这远不是保存多个实体的最佳选择。

对于这种情况,您需要自己实现或使用支持 BulkInsert 的库(通常在后台执行 SqlBulkCopy)

有 3 个主库(2 个免费,1 个专业版)允许批量插入

// Example from Entity Framework Extensions Library
using (var ctx = new EntitiesContext())
{
    ctx.BulkInsert(list);
}

您可以阅读以下文章以了解每个库的优缺点:Entity Framework - Bulk Insert Library Reviews & Comparisons

Entity Framework Extensions 是迄今为止提供最大灵活性(批量插入、更新、删除、合并和 BulkSaveChanges 并支持所有内容)的库,但它是 PRO 版本。如果您正在寻找免费版本,我建议使用 EntityFramework.BulkInsert,但是,它不再受支持并且不支持所有关联和继承。

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

编辑:回答评论问题

I am saving each 100 records, not each record

将一个实体或 100 个实体添加到单元上下文并不重要,Entity Framework 将它们一个一个地保存(每条记录一个插入语句)。只需将 SQL Profiler 与 SQL 服务器数据库一起使用,您就会明白我的意思。

编辑:回答评论问题

great jonathan. is there any way to implement this with ef6 generic uow?

答案取决于您选择使用哪个库。

如果您使用我的库,您可以创建 BulkSaveChanges 方法或在您的 UnitOfWork 中将所有“_context.SaveChanges()”更改为“_context.BulkSaveChanges()”

public void SaveLeadsForBuyer(ISenderModel model)
{
    // ... code ...
    // _unit.SaveChanges();
    _unit.BulkSaveChanges();
}

如果您想要最佳性能并从我的库或免费库中实现批量插入,我可能会添加一个方法或扩展方法(如果您不能更改存储库 class),名为 BulkInsert

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    // ... code ...

    public virtual void BulkInsert(List<TEntity> list)
    {
        _context.BulkInsert(list);
    }
}

请记住,BulkInsert 直接插入实体而无需调用 "SaveChanges",它不使用 context/change 跟踪器来获得最佳性能。