使用dapper.net进行批量插入的最佳方法

时间:2015-03-04 14:20:06

标签: c# .net sql-server bulkinsert dapper

我使用以下代码将记录插入SQL Server 2014中的表

using (SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["myConnString"]))
{

   conn.Execute("INSERT statement here", insertList);

}

insertList是一个包含100万个项目的列表。我在i5桌面上测试了这个插件,在同一台机器上向SQL Server插入了一百万条记录大约需要65分钟。我不确定dapper是如何在幕后进行插入的。我当然不想打开和关闭数百万次数据库连接!

这是在小巧玲珑中进行批量插入的最佳方式,还是应该尝试其他方法,或者使用企业库使用普通的ADO.Net?

修改

事后看来,我知道使用ADO.Net会更好,所以会重新解释我的问题。我仍然想知道这是否是最适合小巧玲珑的人,或者我错过了一个更好的方法来在小巧玲珑本身做到这一点?

5 个答案:

答案 0 :(得分:12)

如果性能是您之后的,那么我建议使用SqlBulkCopy而不是使用Dapper插入。请参阅此处进行一些性能比较:http://www.ikriv.com/dev/db/SqlInsert/SqlInsert.html

答案 1 :(得分:6)

基于Ehsan Sajjad的评论,其中一种方法是编写一个READINLY参数为user-defined TABLE type的存储过程。

假设您要批量插入包含名字和姓氏的联系人,您可以这样做: 1)创建表类型:

CREATE TYPE [dbo].[MyTableType] AS TABLE(
    [FirstName] [varchar](50) NULL,
    [LastName] [varchar](50) NULL
)
GO

2)现在创建一个使用上述表类型的存储过程:

CREATE PROC [dbo].[YourProc]
/*other params here*/
@Names AS MyTableType READONLY
AS
/* proc body here 
 */
GO

3)在.NET端,将参数作为System.Data.SqlDbType.Structured传递    这通常涉及创建内存数据表,然后向其添加行,然后使用此DataTable对象作为@Names参数。 注意:DataTable被认为是内存密集型 - 请小心并对代码进行概要分析,以确保它不会导致服务器出现资源问题。

ALTENATIVE SOLUTION 使用此处列出的方法:https://stackoverflow.com/a/9947259/190476 该解决方案适用于DELETE,但也可以适用于插入或更新。

答案 2 :(得分:3)

以优异的性能插入的最佳免费方式是直接使用SqlBulkCopy类,Alex和Andreas建议。

免责声明:我是该项目的所有者Dapper Plus

此项目不是免费的,但支持以下操作:

  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge

通过使用映射并允许输出像标识列一样的值。

// CONFIGURE & MAP entity
DapperPlusManager.Entity<Order>()
                 .Table("Orders")
                 .Identity(x => x.ID);

// CHAIN & SAVE entity
connection.BulkInsert(orders)
          .AlsoInsert(order => order.Items);
          .Include(x => x.ThenMerge(order => order.Invoice)
                         .AlsoMerge(invoice => invoice.Items))
          .AlsoMerge(x => x.ShippingAddress);   

答案 3 :(得分:0)

第一选择应该是 SQL批量复制,因为它可以安全地进行SQL注入。

但是,有一种方法可以大大提高性能。您可以将多个插入合并到一个SQL中,并且只有一个调用而不是多个调用。 所以代替这个:

enter image description here

您可以拥有:

enter image description here

批量插入用户的代码如下所示:

public async Task InsertInBulk(IList<string> userNames)
{
    var sqls = GetSqlsInBatches(userNames);
    using (var connection = new SqlConnection(ConnectionString))
    {
        foreach (var sql in sqls)
        {
            await connection.ExecuteAsync(sql);
        }
    }
}

private IList<string> GetSqlsInBatches(IList<string> userNames)
{
    var insertSql = "INSERT INTO [Users] (Name, LastUpdatedAt) VALUES ";
    var valuesSql = "('{0}', getdate())";
    var batchSize = 1000;

    var sqlsToExecute = new List<string>();
    var numberOfBatches = (int)Math.Ceiling((double)userNames.Count / batchSize);

    for (int i = 0; i < numberOfBatches; i++)
    {
        var userToInsert = userNames.Skip(i * batchSize).Take(batchSize);
        var valuesToInsert = userToInsert.Select(u => string.Format(valuesSql, u));
        sqlsToExecute.Add(insertSql + string.Join(',', valuesToInsert));
    }

    return sqlsToExecute;
}

此处提供了整篇文章和性能比较:http://www.michalbialecki.com/2019/05/21/bulk-insert-in-dapper/

答案 4 :(得分:-1)

我遇到了一个应该与 ADO、Entity 和 Dapper 一起使用的解决方案问题,因此制作了 this lib;它以

的形式生成批次
IEnumerable<(string SqlQuery, IEnumerable<SqlParameter> SqlParameters)>  
IEnumerable<(string SqlQuery, DynamicParameters DapperDynamicParameters)> 

this link 包含说明。对 SQL 注入是安全的,因为使用参数而不是串联。

与 Dapper 一起使用:

using MsSqlHelpers;
var mapper = new MapperBuilder<Person>()
    .SetTableName("People")
    .AddMapping(person => person.FirstName, columnName: "Name")
    .AddMapping(person => person.LastName, columnName: "Surename")
    .AddMapping(person => person.DateOfBirth, columnName: "Birthday")
    .Build();
var people = new List<Person>()
{ 
    new Person() { FirstName = "John", LastName = "Lennon", DateOfBirth = new DateTime(1940, 10, 9) },
    new Person() { FirstName = "Paul", LastName = "McCartney", DateOfBirth = new DateTime(1942, 6, 18) },
};
var connectionString = "Server=SERVER_ADDRESS;Database=DATABASE_NAME;User Id=USERNAME;Password=PASSWORD;";
var sqlQueriesAndDapperParameters = new MsSqlQueryGenerator().GenerateDapperParametrizedBulkInserts(mapper, people);
using (var sqlConnection = new SqlConnection(connectionString))
{
    // Default batch size: 1000 rows or (2100-1) parameters per insert.
    foreach (var (SqlQuery, DapperDynamicParameters) in sqlQueriesAndDapperParameters)
    {
        sqlConnection.Execute(SqlQuery, DapperDynamicParameters);
    }
}