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

C# 使用dapper.net进行批量插入的最佳方法,c#,.net,sql-server,bulkinsert,dapper,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插

我正在使用以下代码将记录插入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会更好,所以我会重新表述我的问题。我仍然想知道这是dapper能做的最好的事情,还是我错过了一个更好的方法来完成它

基于Ehsan Sajjad的评论,其中一种方法是编写一个存储过程,该存储过程的只读参数为

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

CREATE TYPE [dbo].[MyTableType] AS TABLE(
    [FirstName] [varchar](50) NULL,
    [LastName] [varchar](50) NULL
)
GO
CREATE PROC [dbo].[YourProc]
/*other params here*/
@Names AS MyTableType READONLY
AS
/* proc body here 
 */
GO
2) 现在创建一个使用上述表类型的存储过程:

CREATE TYPE [dbo].[MyTableType] AS TABLE(
    [FirstName] [varchar](50) NULL,
    [LastName] [varchar](50) NULL
)
GO
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被认为是内存密集型的—请小心并分析您的代码,以确保它不会导致服务器上的资源问题

替代解决方案 使用此处概述的方法:
该解决方案用于删除,但也可用于插入或更新。

插入的最佳免费方式是按照Alex和Andreas的建议,直接使用
SqlBulkCopy

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

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

  • 隔板
  • 批量更新
  • 批量删除
  • 大合并
通过使用映射并允许像标识列一样输出值

// 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);   
//配置和映射实体
DapperPlusManager.Entity()
.表格(“订单”)
.Identity(x=>x.ID);
//链和保存实体
连接.批量插入(订单)
.AlsoInsert(订单=>order.Items);
.Include(x=>x.ThenMerge(订单=>order.Invoice)
.AlsoMerge(发票=>invoice.Items))
.AlsoMerge(x=>x.ShippingAddress);

第一个选择应该是SQL批量复制,因为它不受SQL注入的影响

但是,有一种方法可以显著提高性能。您可以将多个插入合并到一个SQL中,并且只有一个调用而不是多个调用。 因此,与此相反:

您可以选择:

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

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;
}
public异步任务InsertInBulk(IList用户名)
{
var sqls=GetSqlsInBatches(用户名);
使用(var连接=新的SqlConnection(ConnectionString))
{
foreach(sql中的var-sql)
{
等待连接。ExecuteAsync(sql);
}
}
}
私有IList GetSqlsInBatches(IList用户名)
{
var insertSql=“插入[用户](名称,LastUpdatedAt)值”;
var valuesSql=“('{0}',getdate())”;
var-batchSize=1000;
var sqlsToExecute=new List();
var numberOfBatches=(int)Math.天花((double)userNames.Count/batchSize);
对于(int i=0;istring.Format(valuesSql,u));
sqlsToExecute.Add(insertSql+string.Join(',',valuesToInsert));
}
返回sqlsToExecute;
}

整篇文章和性能比较都可以在这里找到:

我面临一个解决方案的问题,它应该与ADO、Entity和Dapper一起工作,所以我提出了一个解决方案;它以

IEnumerable<(string SqlQuery, IEnumerable<SqlParameter> SqlParameters)>  
IEnumerable<(string SqlQuery, DynamicParameters DapperDynamicParameters)> 
IEnumerable
数不清
包含说明。它对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);
    }
}
使用MsSqlHelpers;
var mapper=new MapperBuilder()
.SetTableName(“人员”)
.AddMapping(person=>person.FirstName,columnName:“Name”)
.AddMapping(person=>person.LastName,columnName:“Surename”)
.AddMapping(person=>person.DateOfBirth,columnName:“生日”)
.Build();
var people=新列表()
{ 
newperson(){FirstName=“John”,LastName=“Lennon”,DateOfBirth=newdatetime(1940,10,9)},
newperson(){FirstName=“Paul”,LastName=“McCartney”,DateOfBirth=newdatetime(1942,6,18)},
};
var connectionString=“服务器=服务器\地址;数据库=数据库\名称;用户Id=用户名;密码=密码;”;
var sqlQueriesAndDapperParameters=新的MSSQLSQueryGenerator()。GeneratedApperParameterizedBulkinserts(映射器,人);
使用(var sqlConnection=newsqlconnection(connectionString))
{
//默认批量大小:每次插入1000行或(2100-1)个参数。
foreach(sqlQueriesAndDapperParameters中的var(SqlQuery,DapperDynamicParameters)
{
Execute(SqlQuery,DapperDynamicParameters);
}
}

IDataReader一起使用。。。因此,主要的挑战是创建一个方案和一个dataReader,该方案和dataReader使用列名访问属性…使用sp和pass列表访问DB中的用户定义表类型:(其主要缺点是创建一个名为
DataTable
…)@JonathanMagnan,不清楚Dapper Plus是否有免费版本,它与专业版有何不同?Hello@ArsenMkrtchyan,没有“免费”版。但是,您可以无限期地免费试用,直到您准备好生产。我们将很快重新审视我们所有的产品,以明确这一点。啊,我们要走了