Nehmen wir an, ich habe ein gültiges Bedürfnis, direkt einen SQL-Befehl in Entity Framework auszuführen. Ich habe Probleme mit der Verwendung von Parametern in meiner SQL-Anweisung. Das folgende Beispiel (nicht mein wirkliches Beispiel) funktioniert nicht.
var firstName = "John";
var id = 12;
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);
Mit der ExecuteSqlCommand-Methode können Sie keine benannten Parameter wie in ADO.Net übergeben. Die -Dokumentation für diese Methode gibt keine Beispiele für die Ausführung einer parametrisierten Abfrage.
Wie lege ich die Parameter richtig fest?
Es stellt sich heraus, dass dies funktioniert.
var firstName = "John";
var id = 12;
var sql = "Update [User] SET FirstName = {0} WHERE Id = {1}";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);
Versuche dies:
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("@FirstName", firstname),
new SqlParameter("@Id", id));
Du kannst entweder:
1) Übergeben Sie rohe Argumente und verwenden Sie die {0} -Syntax. Z.B:
DbContext.Database.SqlQuery("StoredProcedureName {0}", paramName);
2) Übergeben Sie die Argumente der DbParameter-Unterklasse und verwenden Sie die @ParamName-Syntax.
DbContext.Database.SqlQuery("StoredProcedureName @ParamName",
new SqlParameter("@ParamName", paramValue);
Wenn Sie die erste Syntax verwenden, umgibt EF Ihre Argumente tatsächlich mit DbParamater-Klassen, weist ihnen Namen zu und ersetzt {0} durch den generierten Parameternamen.
Die erste Syntax wird bevorzugt, da Sie keine Factory verwenden müssen oder wissen, welche Art von DbParamaters erstellt werden soll (SqlParameter, OracleParamter usw.).
Die anderen Antworten funktionieren nicht, wenn Sie Oracle verwenden. Sie müssen :
anstelle von @
verwenden.
var sql = "Update [User] SET FirstName = :FirstName WHERE Id = :Id";
context.Database.ExecuteSqlCommand(
sql,
new OracleParameter(":FirstName", firstName),
new OracleParameter(":Id", id));
Versuchen Sie dies (bearbeitet):
ctx.Database.ExecuteSqlCommand(sql, new SqlParameter("FirstName", firstName),
new SqlParameter("Id", id));
Vorherige Idee war falsch.
Für Entity Framework Core 2.0 oder höher lautet der korrekte Weg dazu:
var firstName = "John";
var id = 12;
ctx.Database.ExecuteSqlCommand($"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
Beachten Sie, dass Entity Framework die beiden Parameter für Sie erstellt, sodass Sie vor SQL Injection geschützt sind.
Beachten Sie auch, dass es NICHT ist:
var firstName = "John";
var id = 12;
var sql = $"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
ctx.Database.ExecuteSqlCommand(sql);
dies schützt Sie NICHT vor SQL Injection und es werden keine Parameter erzeugt.
das für mehr.
Vereinfachte Version für Oracle. Wenn Sie OracleParameter nicht erstellen möchten
var sql = "Update [User] SET FirstName = :p0 WHERE Id = :p1";
context.Database.ExecuteSqlCommand(sql, firstName, id);
Für die async-Methode ("ExecuteSqlCommandAsync") können Sie diese wie folgt verwenden:
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)
});
Wenn Ihre zugrunde liegenden Datenbankdatentypen varchar sind, sollten Sie den unten angegebenen Ansatz einhalten. Andernfalls hätte die Abfrage große Auswirkungen auf die Leistung.
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);
Sie können den SQL-Profiler überprüfen, um den Unterschied zu sehen.
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;
}
}
Verwendungszweck:
var names = db.Database.SqlQueryPrm<string>("select name from position_category where [email protected]_key", new { id_key = "mgr" }).ToList();
Gespeicherte Prozeduren können wie folgt ausgeführt werden
string cmd = Constants.StoredProcs.usp_AddRoles.ToString() + " @userId, @roleIdList";
int result = db.Database
.ExecuteSqlCommand
(
cmd,
new SqlParameter("@userId", userId),
new SqlParameter("@roleIdList", roleId)
);
Mehrere Parameter in einer gespeicherten Prozedur mit mehreren Parametern in vb:
Dim result= db.Database.ExecuteSqlCommand("StoredProcedureName @a,@b,@c,@d,@e", a, b, c, d, e)
Für .NET Core 2.2 können Sie FormattableString
für dynamisches SQL verwenden.
//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);