使用 EntityFramework 6 和 Linq 批量更新对象列表

Batch update on object list using EntityFramework 6 and Linq

我想创建一个函数,该函数接受已设置属性的对象列表,并对其进行批量更新或插入,仅对数据库进行 1 次调用。

(更新)这是工作版本:

    public static void BatchInsertProducts(IList<Product> productList)
    {
        using (var context = new DbContext())
        {
            context.Products.AddRange(productList);
            context.SaveChanges();
        }
    }

这是我尝试根据 ID 更新或插入多个项目。我不认为我可以在这里使用 AddRange 所以我这样试了但我收到一个错误:

    public static void SaveMultipleProducts(IList<Product> productList)
    {
        using (var context = new DbContext())
        {
            foreach (Account p in productList)
            {
                if (p.ID == 0)
                {
                    context.Entry(p).State = EntityState.Added;
                    p.InsertUserId = "jtunney";
                    p.InsertDate = DateTime.Now;
                }
                else
                {
                    context.Entry(p).State = EntityState.Modified;
                    p.UpdateUserId = "jtunney";
                    p.UpdateDate = DateTime.Now;
                }
            }
            context.SaveChanges();
        }
    }

不必为每个产品调用保存方法。如果您获取产品并使用相同的 DbContext 保存,您只需调用一次保存,然后它将保存您的所有修改。

想象一下

List<Product> products = Context.Product.ToList();

foreach(var product in products)
{
     product.Price = new Random().Next();
}

Context.Product.SaveChanges();

此代码正在修改列表中所有产品的价格,但由于我们使用相同的上下文来检索结果,并且 EF implements Tracking 通过一次调用 SaveChanges 来保存修改够了

对于批量插入

使用 Entity Framework http://www.entityframeworktutorial.net/EntityFramework6/addrange-removerange.aspx or you can try this library https://efbulkinsert.codeplex.com/ 中的 AddRange 方法。听说过,没用过

删除了 foreach 循环,将 IList<Product> 更改为 IEnumerable<Product>:

public static void BatchInsertProducts(IEnumerable<Product> productList)
{
    using (var context = new DbContext())
    {
        context.Products.AddRange(productList);
        context.SaveChanges();
    }
}

保存多个的一种方法:

public static void SaveMultipleProducts(IEnumerable<Product> productList)
{
    using (var context = new DbContext())
    {
        foreach (Account p in productList)
        {
            p.InsertUserId="jtunney";
            p.InsertDate=DateTime.Now;
            context.Entry(p).State=p.Id==0?EntityState.Added:EntityState.Modified;
        }
        context.SaveChanges();
    }
}

另一种方式:

public static void SaveMultipleProducts(IList<Product> productList)
{
    using (var context = new DbContext())
    {
        foreach (Account p in productList)
        {
            p.InsertUserId="jtunney";
            p.InsertDate=DateTime.Now;
        }
        // Add all records
        context.Products.AddRange(productList);

        // Handle updates
        foreach(var p in productList.Where(p=>p.id!=0))
        {
          context.Entry(p).State=EntityState.Modified;
        }
        context.SaveChanges();
    }
}

This is bit different approach than other....

public static DbContext BatchInsertProducts(DbContext context,IList<Product> productList)
{
    context.Configuration.AutoDetectChangesEnabled = false;
    for (int i = 0; i < lobbies.Count; i += 100)
    {
        context.Set<Product>().AddRange(lobbies.GetRange(i, Math.Min(100, lobbies.Count - i)));
        context.Products.AddRange(productList);
        context.SaveChanges();

        //Dispose and create a context
        context.Dispose();
        context = new DbContext;
        context.Configuration.AutoDetectChangesEnabled = false;   
    }

    return context;
}

上面提到的代码片段做了一些额外的任务以提高性能,我们也可以指定需要的记录数包含在每个批次操作中。

If we want to insert bulk records, creating new context for each batch and dispose then once task is done. very much improve the performance (from minutes to operation to seconds)