web-dev-qa-db-fra.com

Comment effectuer une insertion et renvoyer l'identité insérée avec Dapper?

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
144
ppiotrowicz

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();
247
Marc Gravell

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();
46
jww

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

24
Tadija Bagarić

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();
5
bpruitt-goddard

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();
4
mytydev

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));
        }
    }
0
Lodlaiden

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);
        }
}
0
Wings