实体框架中最快的插入方式

Bon*_*arp 640 c# sql entity-framework

我正在寻找插入Entity Framework的最快方法.

我问这个是因为你有一个活跃的TransactionScope并且插入很大(4000+).它可能持续超过10分钟(事务的默认超时),这将导致事务不完整.

Sla*_*uma 950

您对以下问题的评论中的评论:

"... SavingChanges(每条记录)......"

这是你能做的最糟糕的事情!调用SaveChanges()每条记录可以减慢批量插入速度.我会做一些简单的测试,很可能会提高性能:

  • SaveChanges()ALL记录后调用一次.
  • 调用SaveChanges()例如100条记录.
  • SaveChanges()例如,调用100条记录并处理上下文并创建一个新记录.
  • 禁用更改检测

对于批量插入,我正在尝试使用这样的模式:

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}
Run Code Online (Sandbox Code Playgroud)

我有一个测试程序,它将560.000个实体(9个标量属性,没有导航属性)插入到DB中.使用此代码,它可在不到3分钟的时间内完成.

对于性能而言,SaveChanges()在"许多"记录("许多"大约100或1000)之后调用是很重要的.它还提高了在SaveChanges之后处理上下文并创建新上下文的性能.这清除了所有SaveChanges实体的上下文,不这样做,实体仍然依附于状态中的上下文Unchanged.在上下文中,附加实体的大小越来越大,逐步减慢插入速度.因此,在一段时间后清除它是有帮助的.

以下是我的560.000实体的一些测量:

  • commitCount = 1,recreateContext = false:很多小时(那是你当前的程序)
  • commitCount = 100,recreateContext = false:超过20分钟
  • commitCount = 1000,recreateContext = false:242秒
  • commitCount = 10000,recreateContext = false:202秒
  • commitCount = 100000,recreateContext = false:199秒
  • commitCount = 1000000,recreateContext = false:内存不足异常
  • commitCount = 1,recreateContext = true: 超过10分钟
  • commitCount = 10,recreateContext = true: 241秒
  • commitCount = 100,recreateContext = true: 164秒
  • commitCount = 1000,recreateContext = true: 191秒

上面第一个测试中的行为是性能非常非线性,并且随着时间的推移会极度降低.("许多小时"是估计,我从未完成此测试,我在20分钟后停在50.000个实体.)这种非线性行为在所有其他测试中并不那么重要.

  • @Bongo Sharp:不要忘记在DbContext上设置`AutoDetectChangesEnabled = false;`.它还具有很大的附加性能效果:http://stackoverflow.com/questions/5943394/why-is-inserting-entities-in-ef-4-1-so-slow-compared-to-objectcontext/5943699#5943699 (83认同)
  • 谢谢你的context.Configuration.AutoDetectChangesEnabled = false; 提示,它会产生巨大的**差异. (10认同)
  • 是的,问题是我正在使用Entity Framework 4,而AutoDetectChangesEnabled是4.1的一部分,不过,我做了性能测试,我有惊人的结果,它从00:12:00到00:00:22 SavinChanges每个实体都在做olverload ...非常感谢你的answare!这就是我想要的 (5认同)
  • @dahacker89:您是否使用正确的版本 EF &gt;= 4.1 和 `DbContext`,而不是 `ObjectContext`? (2认同)
  • @ dahacker89:我建议您为您的问题创建一个单独的问题,也许还需要更多详细信息。我无法在这里找出问题所在。 (2认同)
  • 您可以使用 context.ChangeTracker.Clear() 而不是生成新的上下文,如此处所述 /sf/answers/4561450001/ (2认同)

ark*_*nia 174

这种组合足以提高速度.

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
Run Code Online (Sandbox Code Playgroud)

  • 不要盲目地禁用ValidateOnSaveEnabled你可能依赖于那种行为,直到它为时已晚才意识到它.然后,您可能会在代码中的其他位置执行验证,并且再次进行EF验证是完全没必要的. (46认同)
  • @JeremyCook我认为你想要得到的是,如果它解释了从默认值改变这些属性的可能含义(除了性能改进),这个答案会好得多.我同意. (26认同)
  • 在我的测试中,保存 20.000 行的时间从 101 秒减少到 88 秒。不是很多,有什么影响。 (2认同)
  • 这对我有用,但如果您在上下文中更新记录,则需要显式调用 DetectChanges() (2认同)
  • 这些可以被禁用,然后使用try-finally块重新启用:https://msdn.microsoft.com/en-us/data/jj556205.aspx (2认同)

max*_*ego 97

最快的方法是使用我开发的批量插入扩展.

它使用SqlBulkCopy和自定义datareader来获得最大性能.因此,它比使用常规插入或AddRange快20多倍 EntityFramework.BulkInsert与EF AddRange

用法非常简单

context.BulkInsert(hugeAmountOfEntities);
Run Code Online (Sandbox Code Playgroud)

  • 广告越来越聪明......这是付费产品,对于自由职业者而言非常昂贵.被警告! (61认同)
  • 它不是免费的. (54认同)
  • 1年支持和升级600美元?你是不是疯了? (23认同)
  • 太棒了.但是,未插入关系表.在EF 6.1中 (9认同)
  • 快速但只是层次结构的顶层. (9认同)
  • 我不再是产品的所有者了 (7认同)
  • @Muds我还发现了这个库和它使用的MappingAPI库的一些错误.我从[其他答案](http://stackoverflow.com/a/27564352/1730559)的EFUtilities库中取得了更大的成功.它还有其他实用程序,包括批量更新. (6认同)
  • 遗憾的是,这不再是FOSS,在某些时候,所有者将其移至另一个域并开始收取批量插入版本的费用.它不再是一个合适的选择:-( (4认同)
  • 从ZZZ Projects购买codeplex之前,可以在GitHub上找到BulkInsert的多个分支。[此](https://github.com/ghost1face/EntityFramework.BulkInsert)甚至可以在NuGet上找到。 (2认同)
  • 这个功能价格过高. (2认同)
  • 它是免费的,我在ZZZ接手并付费购买之前,根据原始许可发布了原始源代码的更新版本。我从原始站点上的原始项目获得了源代码(不再存在,但是我保存了屏幕截图和所有许可信息)。WayBackMaching FTW!我修复了MappingAPI中处理TPH类型的严重错误,因此它现在可以处理复杂的TPH方案中的共享属性。我还添加了对异步操作的支持(即WriteToServerAsync),并添加了BulkInsertAsync方法。我还添加了对TPC类型的支持。:) (2认同)
  • 如果您使用的是EF Core,则存在。https://github.com/borisdj/EFCore.BulkExtensions (2认同)

Ada*_*kis 80

你应该看看使用System.Data.SqlClient.SqlBulkCopy这个.这是文档,当然还有很多在线教程.

对不起,我知道你正在寻找一个简单的答案让EF去做你想做的事,但批量操作并不是ORM的意思.

  • 我已经使用SqlBulkCopy直接从我的应用程序插入大量数据.你基本上必须创建一个DataTable,填充它,然后将*that*传递给BulkCopy.当你设置你的DataTable时有一些陷阱(其中大部分我已经忘记了,可悲的是),但它应该工作得很好 (7认同)
  • 我在研究这个问题时遇到过几次 SqlBulkCopy,但它似乎更面向表到表的插入,遗憾的是我并没有期待简单的解决方案,而是性能提示,例如管理手动连接,而不是让 EF 为您完成 (2认同)
  • 我做了概念验证,并且正如所承诺的那样,它的工作速度非常快,但我使用EF的原因之一是因为关系数据的插入更容易,例如,如果我插入已经包含关系数据的实体,它还会插入它,你有没有进入这种情况?谢谢! (2认同)
  • 不幸的是,将一个对象网络插入到DBMS中并不是BulkCopy会做的事情.这就是像EF这样的ORM的好处,其成本是不能扩展以有效地执行数百个类似的对象图. (2认同)
  • 如果你需要原始速度或者你将重新运行这个插入,SqlBulkCopy肯定是要走的路.我之前已经插入了数百万条记录,速度非常快.也就是说,除非您需要重新运行此插入,否则使用EF可能更容易. (2认同)

小智 48

我同意Adam Rackis.SqlBulkCopy是将批量记录从一个数据源传输到另一个数据源的最快方法.我用这个来复制20K记录,花了不到3秒钟.看看下面的例子.

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

  • @ZahidMustafa:是的。它正在执行 BulkInsert,而不是批量分析和关系跟踪对象图。如果你想覆盖关系,你必须分析和确定插入顺序,然后批量插入各个级别,并可能更新一些键:需要,您将获得快速定制的解决方案。或者,您可以依靠 EF 来完成此操作,您无需做任何工作,但运行时速度较慢。 (4认同)
  • 我尝试了本文中提供的许多解决方案,SqlBulkCopy 是迄今为止最快的。纯 EF 需要 15 分钟,但通过混合使用该解决方案和 SqlBulkCopy,我能够将时间缩短至 1.5 分钟!这是有 200 万条记录!没有任何数据库索引优化。 (3认同)

小智 20

我建议这篇文章介绍如何使用EF进行批量插入.

实体框架和缓慢的批量INSERT

他探索了这些领域,并对性能进行了比较:

  1. 默认EF(完成添加30,000条记录需要57分钟)
  2. 用ADO.NET代码替换(对于那些相同的30,000 代码,则为25 )
  3. 上下文Bloat-通过为每个工作单元使用新的上下文来保持活动的Context Graph较小(相同的30,000个插入需要33秒)
  4. 大列表 - 关闭AutoDetectChangesEnabled(将时间缩短到大约20秒)
  5. 批量(低至16秒)
  6. DbTable.AddRange() - (性能在12范围内)


Adm*_*vić 18

我已经调查了Slauma的答案(这很棒,谢谢你的想法),我已经减少了批量,直到我达到了最佳速度.看看Slauma的结果:

  • commitCount = 1,recreateContext = true:超过10分钟
  • commitCount = 10,recreateContext = true:241秒
  • commitCount = 100,recreateContext = true:164秒
  • commitCount = 1000,recreateContext = true:191秒

可以看出,当从1移动到10并且从10移动到100时速度增加,但是从100到1000,插入速度再次下降.

所以我专注于将批量大小减少到10到100之间的值时发生的事情,这是我的结果(我使用不同的行内容,因此我的时间具有不同的值):

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141
Run Code Online (Sandbox Code Playgroud)

根据我的结果,批量大小的实际最佳值约为30.它不到10和100.问题是,我不知道为什么30是最优的,也不能找到任何合理的解释.

  • 我发现相同的Postrges和纯SQL(它取决于SQL而不是EF)30是最佳的. (2认同)

Mik*_*son 18

正如其他人所说,如果你想要非常好的插入性能,SqlBulkCopy就是这样做的.

它实现起来有点麻烦,但有些库可以帮助您实现它.有一些,但这次我将无耻地插入我自己的库:https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

您需要的唯一代码是:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }
Run Code Online (Sandbox Code Playgroud)

那么速度有多快?很难说因为它取决于很多因素,计算机性能,网络,对象大小等等.我所做的性能测试表明,25k实体可以在localhost上以标准方式在10s左右插入,如果你优化你的EF配置,在其他答案中提到.EFUtilities大约需要300毫秒.更有趣的是,我使用这种方法在15秒内节省了大约300万个实体,平均每秒约20万个实体.

如果您需要插入相关数据,那么问题就在于此.这可以使用上面的方法有效地进入sql server,但它要求你有一个Id生成策略,让你在父代的app-code中生成id,这样你就可以设置外键.这可以使用GUID或HiLo id生成之类的东西来完成.

  • 与 EntityFramework.BulkInsert 相比,该库仍然免费。+1 (3认同)
  • 只是一个简短的评论来表示感谢!这段代码让我在 1.5 秒内保存了 170k 条记录!完全颠覆了我尝试过的任何其他方法。 (2认同)

小智 14

Dispose()如果您Add()依赖于上下文中其他预加载实体(例如导航属性)的实体,则上下文会产生问题

我使用类似的概念来保​​持我的上下文小,以实现相同的性能

但是不是Dispose()上下文而是重新创建,我只是分离已经存在的实体SaveChanges()

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }
Run Code Online (Sandbox Code Playgroud)

用try catch包装它,TrasactionScope()如果你需要,不要在这里显示它们以保持代码干净


Xav*_*rAM 12

[2019 更新] EF Core 3.1

按照上面所说的,在 EF Core 中禁用 AutoDetectChangesEnabled 效果很好:插入时间除以 100(从几分钟到几秒,具有交叉表关系的 10k 记录)

更新后的代码是:

context.ChangeTracker.AutoDetectChangesEnabled = false;
foreach (IRecord record in records) {
    //Add records to your database        
}
context.ChangeTracker.DetectChanges();
context.SaveChanges();
context.ChangeTracker.AutoDetectChangesEnabled = true; //do not forget to re-enable
Run Code Online (Sandbox Code Playgroud)


Man*_*pel 11

因为这里从来没有提到过,所以我想在这里推荐EFCore.BulkExtensions

context.BulkInsert(entitiesList);                 context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList);                 context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList);                 context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList);         context.BulkInsertOrUpdateAsync(entitiesList);         // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList);                   context.BulkReadAsync(entitiesList);
Run Code Online (Sandbox Code Playgroud)

  • 10 000 个插入从 9 分钟缩短到 12 秒。这值得更多关注! (13认同)
  • 如果有任何方法可以改变公认的答案,那么这应该是现在现代公认的答案。我希望 EF 团队提供开箱即用的功能。 (12认同)
  • 注意:它可能早在 2019 年就已获得 MIT 许可,但现在它使用双重许可证,具体取决于您的用途:请参阅 https://github.com/borisdj/EFCore.BulkExtensions/blob/master/LICENSE.txt (10认同)
  • 我赞同这个建议。在尝试了许多自制解决方案之后,这将我的插入时间从超过 50 秒缩短到了 1 秒。而且,它的 MIT 许可证很容易合并。 (3认同)
  • 这对 ef 6.x 有用吗 (2认同)
  • 仅当实体超过 10 个时,这才比使用 AddRange 更高效 (2认同)
  • 令人难以置信的快速&gt; 30 秒插入 13,000 行到不到一秒,很好。 (2认同)
  • 有4.5这个版本的框架吗? (2认同)

Mic*_*ala 10

是的,SqlBulkUpdate确实是完成此类任务最快的工具。我想在 .NET Core 中找到“最省力”的通用方法,因此我最终使用了来自 Marc Gravell 的名为 FastMember 的优秀库,并为实体框架 DB 上下文编写了一个微小的扩展方法。工作速度快如闪电:

using System.Collections.Generic;
using System.Linq;
using FastMember;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

namespace Services.Extensions
{
    public static class DbContextExtensions
    {
        public static void BulkCopyToServer<T>(this DbContext db, IEnumerable<T> collection)
        {
            var messageEntityType = db.Model.FindEntityType(typeof(T));

            var tableName = messageEntityType.GetSchema() + "." + messageEntityType.GetTableName();
            var tableColumnMappings = messageEntityType.GetProperties()
                .ToDictionary(p => p.PropertyInfo.Name, p => p.GetColumnName());

            using (var connection = new SqlConnection(db.Database.GetDbConnection().ConnectionString))
            using (var bulkCopy = new SqlBulkCopy(connection))
            {
                foreach (var (field, column) in tableColumnMappings)
                {
                    bulkCopy.ColumnMappings.Add(field, column);
                }

                using (var reader = ObjectReader.Create(collection, tableColumnMappings.Keys.ToArray()))
                {
                    bulkCopy.DestinationTableName = tableName;
                    connection.Open();
                    bulkCopy.WriteToServer(reader);
                    connection.Close();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Gui*_*rme 9

我知道这是一个非常古老的问题,但是这里的一个人说开发了一种扩展方法来使用EF的批量插入,当我检查时,我发现该库今天的成本为599美元(对于一个开发人员).也许这对整个库来说都是有意义的,但是对于大量插入来说这太过分了.

这是我制作的一个非常简单的扩展方法.我首先使用它与数据库配对(不要先用代码测试,但我认为它的工作方式相同).YourEntities使用您的上下文名称进行更改:

public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            await conn.OpenAsync();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            await bulkCopy.WriteToServerAsync(table);
        }
    }

    public void BulkInsertAll<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            conn.Open();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            bulkCopy.WriteToServer(table);
        }
    }

    public string GetTableName(Type type)
    {
        var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace;
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        var table = mapping
            .EntityTypeMappings.Single()
            .Fragments.Single()
            .StoreEntitySet;

        return (string)table.MetadataProperties["Table"].Value ?? table.Name;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以对任何继承的集合使用它IEnumerable,如下所示:

await context.BulkInsertAllAsync(items);
Run Code Online (Sandbox Code Playgroud)

  • 它已经在这里:`await bulkCopy.WriteToServerAsync(table);` (2认同)

Jon*_*nan 6

我正在寻找插入实体框架的最快方法

有一些支持批量插入的第三方库可用:

  • Z.EntityFramework.Extensions(推荐
  • EFU实用程序
  • EntityFramework.BulkInsert

请参阅:实体框架批量插入库

选择批量插入库时要小心。只有实体框架扩展支持所有类型的关联和继承,并且它是唯一仍然受支持的一种。


免责声明:我是实体框架扩展的所有者

该库允许您执行场景所需的所有批量操作:

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

例子

// 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;
});
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很棒的扩展,但**不是免费的**。 (24认同)
  • 是的,批量插入 600 美元。完全值得。 (7认同)
  • @eocron Yeat 如果您将其用于商业用途,那么这是值得的。我不认为 600 美元有什么问题,因为我不需要自己花几个小时来构建它,这将花费我比 600 美元更多的东西。是的,这要花钱,但看看我的小时费率,这笔钱花得值! (4认同)
  • 这个答案非常好,[EntityFramework.BulkInsert](https://www.nuget.org/packages/EntityFramework.BulkInsert-ef6-ext) 在 1.5 秒内执行批量插入 15K 行,对于内部进程来说效果非常好就像 Windows 服务一样。 (2认同)

Rez*_*abi 6

保存列表的最快方法之一,您必须应用以下代码

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
Run Code Online (Sandbox Code Playgroud)

AutoDetectChangesEnabled = false

添加、添加范围和保存更改:不检测更改。

ValidateOnSaveEnabled = false;

不检测更改跟踪器

你必须添加nuget

Install-Package Z.EntityFramework.Extensions
Run Code Online (Sandbox Code Playgroud)

现在您可以使用以下代码

var context = new MyContext();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

context.BulkInsert(list);
context.BulkSaveChanges();
Run Code Online (Sandbox Code Playgroud)

  • Z 库不是免费的 (10认同)

Max*_*xim 5

尝试使用存储过程来获取要插入的数据的XML.

  • 如果您不想将数据存储为XML,则不需要将数据作为XML传递.在SQL 2008中,您可以使用表值参数. (9认同)

Sge*_*dda 5

我对上面的@Slauma 的例子做了一个通用的扩展;

public static class DataExtensions
{
    public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator)
    {
        context.Set(typeof(T)).Add((T)entity);

        if (count % commitCount == 0)
        {
            context.SaveChanges();
            if (recreateContext)
            {
                context.Dispose();
                context = contextCreator.Invoke();
                context.Configuration.AutoDetectChangesEnabled = false;
            }
        }
        return context;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

public void AddEntities(List<YourEntity> entities)
{
    using (var transactionScope = new TransactionScope())
    {
        DbContext context = new YourContext();
        int count = 0;
        foreach (var entity in entities)
        {
            ++count;
            context = context.AddToContext<TenancyNote>(entity, count, 100, true,
                () => new YourContext());
        }
        context.SaveChanges();
        transactionScope.Complete();
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

378868 次

最近记录:

5 年,11 月 前