Как выполнить вставку и вернуть вставленную личность с помощью Dapper? - PullRequest
156 голосов
/ 25 ноября 2011

Как выполнить вставку в базу данных и вернуть вставленную идентификацию с помощью Dapper?

Я пробовал что-то вроде этого:

string sql = "DECLARE @ID int; " +
             "INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); " +
             "SELECT @ID = SCOPE_IDENTITY()";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).First();

Но это не сработало.

@ Марк Гравелл, спасибо за ответ. Я пробовал ваше решение, но все еще та же самая трассировка исключений ниже

System.InvalidCastException: Specified cast is not valid

at Dapper.SqlMapper.<QueryInternal>d__a`1.MoveNext() in (snip)\Dapper\SqlMapper.cs:line 610
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in (snip)\Dapper\SqlMapper.cs:line 538
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param) in (snip)\Dapper\SqlMapper.cs:line 456

Ответы [ 7 ]

255 голосов
/ 25 ноября 2011

Он поддерживает , поддерживает параметры ввода / вывода (включая значение RETURN), если вы используете DynamicParameters, но в этом случае проще просто:

string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() as int)";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
46 голосов
/ 27 сентября 2012

КБ: 2019779 , "Вы можете получить неправильные значения при использовании SCOPE_IDENTITY () и @@ IDENTITY", Предложение OUTPUT - самый безопасный механизм:

string sql = @"
DECLARE @InsertedRows AS TABLE (Id int);
INSERT INTO [MyTable] ([Stuff]) OUTPUT Inserted.Id INTO @InsertedRows
VALUES (@Stuff);
SELECT Id FROM @InsertedRows";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
31 голосов
/ 04 ноября 2017

Поздний ответ, но вот альтернатива ответам SCOPE_IDENTITY(), которые мы в итоге использовали: ВСТАВЛЕННЫЙ ВЫХОД

Возврат только IDвставленного объекта:

Позволяет получить все или некоторые атрибуты вставленной строки:

string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email)
                        OUTPUT INSERTED.[Id]
                        VALUES(@Username, @Phone, @Email);";

int newUserId = conn.QuerySingle<int>(insertUserSql,
                                new
                                {
                                    Username = "lorem ipsum",
                                    Phone = "555-123",
                                    Email = "lorem ipsum"
                                }, tran);

Возвращать вставленный объект с идентификатором:

Если вы хотите, вы можете получить Phone и Email или даже весь вставленный ряд:

string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email)
                        OUTPUT INSERTED.*
                        VALUES(@Username, @Phone, @Email);";

User newUser = conn.QuerySingle<User>(insertUserSql,
                                new
                                {
                                    Username = "lorem ipsum",
                                    Phone = "555-123",
                                    Email = "lorem ipsum"
                                }, tran);

Кроме того, вы можете вернуть данные удалено или обновлено строк.Просто будьте осторожны, если вы используете триггеры, потому что:

Столбцы, возвращаемые из OUTPUT, отражают данные в том виде, в каком они есть после выполнения оператора INSERT, UPDATE или DELETE, но до выполнения триггеров.

Для триггеров INSTEAD OF возвращаемые результаты генерируются так, как если бы фактически имели место INSERT, UPDATE или DELETE, даже если в результате операции триггера не было никаких изменений.Если в теле триггера используется оператор, включающий предложение OUTPUT, псевдонимы таблиц должны использоваться для ссылки на вставленные и удаленные таблицы триггера, чтобы избежать дублирования ссылок на столбцы с таблицами INSERTED и DELETED, связанными с OUTPUT.

Подробнее об этом в документации: ссылка

5 голосов
/ 29 июня 2012

Исключение InvalidCastException, которое вы получаете, связано с тем, что SCOPE_IDENTITY представляет собой Десятичное число (38,0) .

Вы можете вернуть его как int, приведя его следующим образом:

string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() AS INT)";

int id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
4 голосов
/ 19 апреля 2012

Не уверен, что это потому, что я работаю с SQL 2000 или нет, но мне пришлось сделать это, чтобы заставить его работать.

string sql = "DECLARE @ID int; " +
             "INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); " +
             "SET @ID = SCOPE_IDENTITY(); " +
             "SELECT @ID";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
0 голосов
/ 12 февраля 2019

Есть отличная библиотека, которая сделает вашу жизнь проще. Dapper.Contrib.Extensions. После включения этого вы можете просто написать:

public int Add(Transaction transaction)
{
        using (IDbConnection db = Connection)
        {
                return (int)db.Insert(transaction);
        }
}
0 голосов
/ 22 августа 2016

Если вы используете Dapper.SimpleSave:

 //no safety checks
 public static int Create<T>(object param)
    {
        using (SqlConnection conn = new SqlConnection(GetConnectionString()))
        {
            conn.Open();
            conn.Create<T>((T)param);
            return (int) (((T)param).GetType().GetProperties().Where(
                    x => x.CustomAttributes.Where(
                        y=>y.AttributeType.GetType() == typeof(Dapper.SimpleSave.PrimaryKeyAttribute).GetType()).Count()==1).First().GetValue(param));
        }
    }
...