У меня есть дочерняя таблица, которая ссылается на родительскую таблицу с отношением 1 ко многим. ПК -> несколько внешних ключей. Причина в том, что таблица FK представляет собой список объектов, связанных с идентификатором в родительской таблице.
Как я могу использовать dapper для вставки нескольких строк с одним и тем же 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;
}
}
Кажется, я неправильно понял вопрос :o) - Можете ли вы добавить дополнительную информацию о структуре таблицы, где мы можем видеть PK и FK, и о дизайне двух связанных классов, где мы также видим PK и FK, которые вы хотите использовать.
Есть решение с Dapper-Plus dapper-plus.net/…
@SirRufo Это стоит денег
Вы про Dapper-Plus или "добавьте еще немного информации"?
@SirRufo "Dapper"-Plus не имеет ничего общего с Dapper. Кто-то похитил имя и отравил поисковые индексы, чтобы вместо настоящего Dapper отображались их ошибочные недокументированные продукты и продукты с закрытым исходным кодом. У создателей Dapper есть очень сильные мысли по этому поводу, начиная с At least fix the doc errors!
.
@ CodeMan03, в чем собственно вопрос? Как получить значение LocationInformation.Id
? connection.ExecuteAsync(...,locationInformationModels)
уже вставляет каждый элемент по одному. Dapper не будет переписывать ваш SQL-запрос, чтобы он принимал несколько строк. Если вы хотите получить идентификатор, вам нужно использовать предложение OUTPUT
и использовать QueryAsync
вместо ExecuteAsync
Если я правильно вас понял, вы хотите делать вставки в таблицу партиями. Pure Dapper не имеет встроенного способа выполнения массовых вставок, поэтому вы можете либо использовать Dapper-Plus, либо SqlBulkCopy для всех массовых действий, которые вы хотите выполнить.
С помощью SqlBulkCopy вы можете настроить динамическое сопоставление столбцов с использованием отражения, чтобы избежать явного определения всех столбцов.
Вопрос немного не ясен. Код показывает вставку списка элементов без чего-либо похожего на внешний ключ. В любом случае, Dapper — это микроORM, он не обрабатывает отношения между сущностями или ключи автообновления. Он также не изменит предоставленный оператор SQL для обработки нескольких объектов.
Я подозреваю, что реальный вопрос заключается в том, как вернуть сгенерированное значение из базы данных, например, ключ IDENTITY
, хотя код показывает, что LocationInformationId
предоставляется приложением. Для этого запрос нужно будет изменить, чтобы он возвращал любое сгенерированное значение, например, с предложением OUTPUT
.
Возврат сгенерированных значений
Например, это вернет автоматически сгенерированный идентификатор:
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;
}
Конструкторы строк
Можно вставить несколько строк с помощью одного INSERT с помощью конструкторов строк, но для этого требуется динамическое создание 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
""";
ToDataTable от MoreLinq можно использовать для преобразования входных элементов в DataTable. После этого вызов QueryAsync
выполнит INSERT и вернет список выходов:
var table=items.ToDataTable();
var newIds=await connection.QueryAsync(sql,table);
Итак, если у меня есть 1000 строк. Я вызываю базу данных 1000 раз? Это бессмысленно