Supposons simplement que j'ai un besoin valide d'exécuter directement une commande SQL dans Entity Framework. Je ne parviens pas à comprendre comment utiliser les paramètres dans mon instruction SQL. L'exemple suivant (pas mon exemple réel) ne fonctionne pas.
var firstName = "John";
var id = 12;
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);
La méthode ExecuteSqlCommand ne vous permet pas de passer des paramètres nommés comme dans ADO.Net et la documentation de cette méthode ne donne aucun exemple sur la façon d'exécuter une requête paramétrée.
Comment spécifier les paramètres correctement?
Il s'avère que cela fonctionne.
var firstName = "John";
var id = 12;
var sql = "Update [User] SET FirstName = {0} WHERE Id = {1}";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);
Essaye ça:
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("@FirstName", firstname),
new SqlParameter("@Id", id));
Tu peux soit:
1) Passez des arguments bruts et utilisez la syntaxe {0}. Par exemple:
DbContext.Database.SqlQuery("StoredProcedureName {0}", paramName);
2) Passez les arguments de la sous-classe DbParameter et utilisez la syntaxe @ParamName.
DbContext.Database.SqlQuery("StoredProcedureName @ParamName",
new SqlParameter("@ParamName", paramValue);
Si vous utilisez la première syntaxe, EF encapsulera vos arguments avec les classes DbParamater, leur attribuera des noms et remplacera {0} par le nom de paramètre généré.
La première syntaxe est préférable si vous n'avez pas besoin d'utiliser une fabrique ni de savoir quel type de DbParamaters créer (SqlParameter, OracleParamter, etc.).
Les autres réponses ne fonctionnent pas avec Oracle. Vous devez utiliser :
au lieu de @
.
var sql = "Update [User] SET FirstName = :FirstName WHERE Id = :Id";
context.Database.ExecuteSqlCommand(
sql,
new OracleParameter(":FirstName", firstName),
new OracleParameter(":Id", id));
Essayez ceci (édité):
ctx.Database.ExecuteSqlCommand(sql, new SqlParameter("FirstName", firstName),
new SqlParameter("Id", id));
L'idée précédente était fausse.
Pour l'entité Framework Core 2.0 ou une version ultérieure, procédez comme suit:
var firstName = "John";
var id = 12;
ctx.Database.ExecuteSqlCommand($"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
Notez qu'Entity Framework produira les deux paramètres pour vous, de sorte que vous êtes protégé de Sql Injection.
Notez également que ce n'est pas:
var firstName = "John";
var id = 12;
var sql = $"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
ctx.Database.ExecuteSqlCommand(sql);
car cela ne vous protège PAS de Sql Injection et aucun paramètre n’est produit.
Voir ceci pour plus.
Version simplifiée pour Oracle. Si vous ne voulez pas créer OracleParameter
var sql = "Update [User] SET FirstName = :p0 WHERE Id = :p1";
context.Database.ExecuteSqlCommand(sql, firstName, id);
Pour la méthode async ("ExecuteSqlCommandAsync"), vous pouvez l'utiliser comme ceci:
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
await ctx.Database.ExecuteSqlCommandAsync(
sql,
parameters: new[]{
new SqlParameter("@FirstName", firstname),
new SqlParameter("@Id", id)
});
Si vos types de données de base de données sous-jacents sont varchar, vous devez vous en tenir à l’approche ci-dessous. Sinon, la requête aurait un impact énorme sur les performances.
var firstName = new SqlParameter("@firstName", System.Data.SqlDbType.VarChar, 20)
{
Value = "whatever"
};
var id = new SqlParameter("@id", System.Data.SqlDbType.Int)
{
Value = 1
};
ctx.Database.ExecuteSqlCommand(@"Update [User] SET FirstName = @firstName WHERE Id = @id"
, firstName, id);
Vous pouvez vérifier le profileur SQL pour voir la différence.
public static class DbEx {
public static IEnumerable<T> SqlQueryPrm<T>(this System.Data.Entity.Database database, string sql, object parameters) {
using (var tmp_cmd = database.Connection.CreateCommand()) {
var dict = ToDictionary(parameters);
int i = 0;
var arr = new object[dict.Count];
foreach (var one_kvp in dict) {
var param = tmp_cmd.CreateParameter();
param.ParameterName = one_kvp.Key;
if (one_kvp.Value == null) {
param.Value = DBNull.Value;
} else {
param.Value = one_kvp.Value;
}
arr[i] = param;
i++;
}
return database.SqlQuery<T>(sql, arr);
}
}
private static IDictionary<string, object> ToDictionary(object data) {
var attr = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(attr)) {
if (property.CanRead) {
dict.Add(property.Name, property.GetValue(data, null));
}
}
return dict;
}
}
Usage:
var names = db.Database.SqlQueryPrm<string>("select name from position_category where id_key=@id_key", new { id_key = "mgr" }).ToList();
Les procédures stockées peuvent être exécutées comme ci-dessous
string cmd = Constants.StoredProcs.usp_AddRoles.ToString() + " @userId, @roleIdList";
int result = db.Database
.ExecuteSqlCommand
(
cmd,
new SqlParameter("@userId", userId),
new SqlParameter("@roleIdList", roleId)
);
Plusieurs paramètres dans une procédure stockée comportant plusieurs paramètres dans vb:
Dim result= db.Database.ExecuteSqlCommand("StoredProcedureName @a,@b,@c,@d,@e", a, b, c, d, e)
Pour .NET Core 2.2, vous pouvez utiliser FormattableString
pour le SQL dynamique.
//Assuming this is your dynamic value and this not coming from user input
var tableName = "LogTable";
// let's say target date is coming from user input
var targetDate = DateTime.Now.Date.AddDays(-30);
var param = new SqlParameter("@targetDate", targetDate);
var sql = string.Format("Delete From {0} Where CreatedDate < @targetDate", tableName);
var froamttedSql = FormattableStringFactory.Create(sql, param);
_db.Database.ExecuteSqlCommand(froamttedSql);