Dapper, как вставить несколько строк с одним и тем же внешним ключом

У меня есть дочерняя таблица, которая ссылается на родительскую таблицу с отношением 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;
                }        
            }

Итак, если у меня есть 1000 строк. Я вызываю базу данных 1000 раз? Это бессмысленно

CodeMan03 19.02.2023 06:25

Кажется, я неправильно понял вопрос :o) - Можете ли вы добавить дополнительную информацию о структуре таблицы, где мы можем видеть PK и FK, и о дизайне двух связанных классов, где мы также видим PK и FK, которые вы хотите использовать.

Sir Rufo 19.02.2023 08:51

Есть решение с Dapper-Plus dapper-plus.net/…

Sir Rufo 19.02.2023 09:09

@SirRufo Это стоит денег

CodeMan03 21.02.2023 23:23

Вы про Dapper-Plus или "добавьте еще немного информации"?

Sir Rufo 22.02.2023 06:44

@SirRufo "Dapper"-Plus не имеет ничего общего с Dapper. Кто-то похитил имя и отравил поисковые индексы, чтобы вместо настоящего Dapper отображались их ошибочные недокументированные продукты и продукты с закрытым исходным кодом. У создателей Dapper есть очень сильные мысли по этому поводу, начиная с At least fix the doc errors!.

Panagiotis Kanavos 22.02.2023 09:58

@ CodeMan03, в чем собственно вопрос? Как получить значение LocationInformation.Id? connection.ExecuteAsync(...,locationInformationModels) уже вставляет каждый элемент по одному. Dapper не будет переписывать ваш SQL-запрос, чтобы он принимал несколько строк. Если вы хотите получить идентификатор, вам нужно использовать предложение OUTPUT и использовать QueryAsync вместо ExecuteAsync

Panagiotis Kanavos 22.02.2023 10:09
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если я правильно вас понял, вы хотите делать вставки в таблицу партиями. Pure Dapper не имеет встроенного способа выполнения массовых вставок, поэтому вы можете либо использовать Dapper-Plus, либо SqlBulkCopy для всех массовых действий, которые вы хотите выполнить.

С помощью SqlBulkCopy вы можете настроить динамическое сопоставление столбцов с использованием отражения, чтобы избежать явного определения всех столбцов.

Вот один из способов настроить SqlBulkCopy, который можно модифицировать для вставки вместо удаления.
FinneVirta 22.02.2023 09:57
Ответ принят как подходящий

Вопрос немного не ясен. Код показывает вставку списка элементов без чего-либо похожего на внешний ключ. В любом случае, 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);

Другие вопросы по теме