英文:
Dapper how to insert multiple rows with same foreign key
问题
我有一个子表,它引用了一个具有一对多关系的父表。
主键 -> 多个外键。原因是外键表是与父表上的ID相关的对象列表。
如何使用Dapper插入具有相同外键的多行?
assessmentModel.locationInformationModels = new List<LocationInformationModel>();
string sqlStatement = @"
INSERT INTO LocationInformation
([LocationInformationId]
,[Address1]
,[Address2]
,[City]
,[State]
,[Zip])
VALUES (
@LocationInformationId, @Address1, @Address2, @City, @State, @Zip)";
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync();
using (var ts = connection.BeginTransaction())
{
var retVal = await connection.ExecuteAsync(sqlStatement, assessmentModel.locationInformationModels, transaction: ts);
ts.Commit();
return retVal;
}
}
请注意,这段代码似乎尝试将 assessmentModel.locationInformationModels
中的多个对象插入到 "LocationInformation" 表中,每个对象都具有相同的外键值。
英文:
I have a child table that rerferences a parent table with a 1 to many relationship.
PK -> multiple foreign keys. The reason is the FK table are a list of objects related to an ID on the parent table.
How can I use dapper to insert multiple rows with the same FK?
assessmentModel.locationInformationModels = new List<LocationInformationModel>();
string sqlStatement = @"INSERT INTO LocationInformation
([LocationInformationId]
,[Address1]
,[Address2]
,[City]
,[State]
,[Zip])
VALUES(
@LocationInformationId,@Address1,@Address2,@City,@State,@Zip)";
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync();
using (var ts = connection.BeginTransaction())
{
var retVal = await connection.ExecuteAsync(sqlStatement, assessmentModel.locationInformationModels, transaction:ts);
ts.Commit();
return retVal;
}
}
答案1
得分: 3
问题有点不太清楚。代码显示了插入一系列项的操作,但没有看到任何类似外键的东西。无论如何,Dapper是一个微型ORM,它不处理实体之间的关系,也不自动更新键。它不会更改提供的SQL语句以处理多个对象。
我怀疑真正的问题是如何从数据库返回生成的值,例如IDENTITY
键,尽管代码显示LocationInformationId
由应用程序提供。为了做到这一点,查询将不得不修改以返回任何生成的值,例如使用OUTPUT
子句。
返回生成的值
例如,这将返回自动生成的ID:
var sql = @"""
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
OUTPUT inserted.LocationInformationId
VALUES(@Address1,@Address2,@City,@State,@Zip)
""";
问题的代码已经使用ExecuteAsync(sql, IEnumerable<T>, ...)
来插入多个项。这将为每个项执行一次查询,但不会返回任何结果。没有QueryAsync
的等效方法,因此应用程序将不得不显式执行INSERT:
foreach (var item in items)
{
var newId = await connection.QueryAsync(sql, item);
item.Id = newId;
}
行构造器
可以使用行构造器一次性插入多行,但这需要动态生成SQL查询:
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
VALUES
OUTPUT inserted.LocationInformationId
(@Address1_01, @Address2_01, @City_01, @State_01, @Zip_01),
(@Address1_02, @Address2_02, @City_02, @State_02, @Zip_02),
(@Address1_03, @Address2_03, @City_03, @State_03, @Zip_03),
...
所有项目的值也必须由应用程序转换为参数列表。这并不是很方便。
表值参数
另一种选择是使用表值参数类型,一次性插入传递为DataTable的多个值。这需要在数据库中创建一个表值参数类型,并将数据转换为DataTable,例如:
CREATE TYPE dbo.LocationTableType AS TABLE
( Address1 nvarchar(50), ... )
查询应更改为使用表参数的INSERT ... SELECT
:
var sql = @"""
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
OUTPUT inserted.LocationInformationId
SELECT Address1, Address2, City, State, Zip
FROM @table
""";
MoreLinq的ToDataTable可用于将输入项转换为DataTable。然后,QueryAsync
调用将执行INSERT并返回输出列表:
var table = items.ToDataTable();
var newIds = await connection.QueryAsync(sql, table);
英文:
The question is a bit unclear. The code shows inserting a list of items without anything that looks like a foreign key. In any case, Dapper is a microORM, it doesn't handle relations between entities, or auto-update keys. It won't change the supplied SQL statement to handle multiple objects either.
I suspect the real question is how to return a generated value from the database, eg an IDENTITY
key, although the code shows that LocationInformationId
is supplied by the application. To do this, the query will have to be modified to return any generated value, eg with the OUTPUT
clause.
Returning generated values
For example, this will return the auto-generated ID :
var sql = """
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
OUTPUT inserted.LocationInformationId
VALUES(@Address1,@Address2,@City,@State,@Zip)
""";
The question's code already uses ExecuteAsync(sql, IEnumerable<T>,...)
to insert multiple items. That executes the query once for every item but doesn't return any results. There's no equivalent for QueryAsync
, so the application will have to execute the INSERTs explicitly :
foreach(var item in items)
{
var newId=await connection.QueryAsync(sql,item);
item.Id=newId;
}
Row constructors
It's possible to insert multiple rows with a single INSERT using row constructors, but that requires generating the SQL query dynamically :
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
VALUES
OUTPUT inserted.LocationInformationId
(@Address1_01,@Address2_01,@City_01,@State_01,@Zip_01),
(@Address1_02,@Address2_02,@City_02,@State_02,@Zip_02),
(@Address1_03,@Address2_03,@City_03,@State_03,@Zip_03),
...
All item values will have to be flattened by the application to a list of parameters too. Not very helpful.
Table valued parameters
Another option is to use a table-valued parameter type parameter to insert multiple values passed as a DataTable at once. This requires creating a table valued parameter type in the database and converting the data into a DataTable, eg :
CREATE TYPE dbo.LocationTableType AS TABLE
( Address1 nvarchar(50), ... )
The query should change to an INSERT ... SELECT
using the table parameter :
var sql = """
INSERT INTO LocationInformation
([Address1]
,[Address2]
,[City]
,[State]
,[Zip])
OUTPUT inserted.LocationInformationId
SELECT Address1,Address2,City,State,Zip
FROM @table
""";
MoreLinq's ToDataTable can be used to convert the input items to a DataTable. After that, a QueryAsync
call will execute the INSERT and return the list of outputs :
var table=items.ToDataTable();
var newIds=await connection.QueryAsync(sql,table);
答案2
得分: 0
如果我理解正确,您想要批量在表中插入数据。纯粹的Dapper没有内置的批量插入功能,因此您的选择要么是使用Dapper-Plus,要么是使用SqlBulkCopy来执行所有批量操作。
使用SqlBulkCopy,您可以设置动态的列映射,通过反射来避免需要显式定义所有列。
英文:
If I understand you correctly you want to do inserts on the table in batches. Pure Dapper does not have any built in way to do bulk inserts, so your options would be either using Dapper-Plus or SqlBulkCopy for all bulk actions you want to perform.
With SqlBulkCopy you could set up a dynamic columnmapping using reflection to avoid having to define all columns explicitly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论