Comment effectuer une insertion dans la base de données et renvoyer l'identité insérée avec Dapper?
J'ai essayé quelque chose comme ça:
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();
Mais ça n'a pas marché.
@Marc Gravell merci pour la réponse .. j'ai déjà essayé votre solution, mais la même trace d'exception se trouve ci-dessous
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
Cela prend support paramètres d'entrée/sortie (y compris la valeur RETURN
) si vous utilisez DynamicParameters
, mais dans ce cas, l'option la plus simple est simplement:
string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() as int)";
var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
KB: 2019779 , "Des valeurs incorrectes peuvent être générées lors de l'utilisation de SCOPE_IDENTITY () et @@ IDENTITY", La clause OUTPUT est le mécanisme le plus sûr:
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();
Une réponse tardive, mais voici une alternative à la réponse SCOPE_IDENTITY()
que nous avons finalement utilisée: OUTPUT INSERTED
Retourne uniquement l'ID de l'objet inséré:
Il vous permet d’obtenir tout ou partie des attributs de la ligne insérée:
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);
Retourne l'objet inséré avec l'ID:
Si vous le souhaitez, vous pouvez obtenir Phone
et Email
ou même toute la ligne insérée:
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);
De plus, vous pouvez également renvoyer des données de delete ou updated lignes. Faites attention si vous utilisez des déclencheurs pour les raisons suivantes:
Les colonnes renvoyées par OUTPUT reflètent les données telles qu'elles sont après le Les instructions INSERT, UPDATE ou DELETE sont terminées mais avant les déclencheurs sont exécutés.
Pour les déclencheurs INSTEAD OF, les résultats renvoyés sont générés comme si le INSERT, UPDATE ou DELETE s’est réellement produit, même si non les modifications ont lieu à la suite de l'opération de déclenchement. Si un déclaration qui inclut une clause OUTPUT est utilisée dans le corps d’un trigger, les alias de table doivent être utilisés pour référencer le trigger inséré et des tables supprimées pour éviter la duplication des références de colonne avec le Tables INSERTED et DELETED associées à OUTPUT.
Plus d'informations dans la documentation: link
L'exception InvalidCastException que vous obtenez est due à SCOPE_IDENTITY étant un décimal (38,0) .
Vous pouvez le retourner en tant qu'int en le lançant comme suit:
string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() AS INT)";
int id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
Pas sûr que ce soit parce que je travaille avec SQL 2000 ou non, mais je devais le faire pour que cela fonctionne.
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();
Si vous utilisez 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));
}
}
Il existe une excellente bibliothèque pour vous faciliter la vie, Dapper.Contrib.Extensions. Après avoir inclus ceci, vous pouvez simplement écrire:
public int Add(Transaction transaction)
{
using (IDbConnection db = Connection)
{
return (int)db.Insert(transaction);
}
}