实体框架批量插入虚幻慢

时间:2016-03-27 09:16:51

标签: c# entity-framework azure azure-webjobs

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

问题是需要11个小时来保存50.000个实体,它仍然落后。我正在使用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();
        }
    }
}

1 个答案:

答案 0 :(得分:5)

这很慢,因为Entity Framework为每条记录执行数据库往返。因此,如果您节省了200,000个实体,那么将执行 200,000次数据库往返,这对于保存多个实体来说是最佳的。

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

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

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

您可以阅读以下文章,了解PROS&amp;每个图书馆的缺点:Entity Framework - Bulk Insert Library Reviews & Comparisons

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

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

编辑:回答评论问题

  

我保存每100条记录,而不是每条记录

如果向单元上下文添加一个实体或100个实体并不重要,实体框架会逐个保存它们(每个记录都有一个插入语句)。只需将SQL事件探查器与SQL Server数据库一起使用,您就会明白我的意思。

编辑:回答评论问题

  

伟大的乔纳森。有没有办法用ef6 generic实现这个   UOW?

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

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

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

如果你想从我的库或免费库中获得最佳性能和实现批量插入,我可能会添加名为BulkInsert的方法或扩展方法(如果你不能更改存储库类)

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

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

请记住BulkInsert直接插入实体而不必调用“SaveChanges”,它不会使用上下文/更改跟踪器来获得最佳性能。