247

Let's just suppose I have a valid need for directly executing a sql command in Entity Framework. I am having trouble figuring out how to use parameters in my sql statement. The following example (not my real example) doesn't work.

var firstName = "John";
var id = 12;
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);

The ExecuteSqlCommand method doesn't allow you to pass in named parameters like in ADO.Net and the documentation for this method doesn't give any examples on how to execute a parameterized query.

How do I specify the parameters correctly?

jessegavin
  • 74,067
  • 28
  • 136
  • 164

14 Answers14

338

Try this:

var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";

ctx.Database.ExecuteSqlCommand(
    sql,
    new SqlParameter("@FirstName", firstname),
    new SqlParameter("@Id", id));
tomexou
  • 343
  • 2
  • 5
Robert te Kaat
  • 3,381
  • 1
  • 13
  • 2
  • 3
    This really should be the answer marked correct, the one above is prone to attacks and is not best practices. – Min Oct 28 '14 at 20:52
  • 10
    @Min, the accepted answer is no more prone to attacks than this answer. Maybe you thought it was using string.Format - it's not. – Simon MᶜKenzie Nov 25 '14 at 04:35
  • 1
    This should be marked as answer! This is the one and only correct answer because it works with DateTime. – Sven Jan 07 '15 at 13:43
232

Turns out that this works.

var firstName = "John";
var id = 12;
var sql = "Update [User] SET FirstName = {0} WHERE Id = {1}";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
jessegavin
  • 74,067
  • 28
  • 136
  • 164
  • 15
    This will work, but this mechanism allows for SQL injection and also prevents the database from reusing an execution plan when the statement shows up again but with different values. – Greg Biles Nov 04 '11 at 16:52
  • 106
    @GregB I do not think you are correct here. I have tested that it won't allow me to, for instance, terminate a string literal early. Moreover, I looked at the source code and found that it's using DbCommand.CreateParameter to wrap up any raw values into parameters. So no SQL injection, and a nice succinct method invocation. – Josh Gallagher Nov 17 '11 at 11:52
  • 7
    @JoshGallagher Yes, you're right. I was thinking of a string.Format scenario putting this together. – Greg Biles Dec 07 '11 at 22:07
  • 6
    It does NOT work as of SQL Server 2008 R2. You'll have @p0, @p2, ..., @pN instead of parameters you passed. Use `SqlParameter("@paramName", value)` instead. – Arnthor Jun 19 '14 at 10:13
  • Can't believe that no one has mentioned the performance impact of this query if the database table columns are specified as ANSI varchar. .Net strings are unicode means that paramaters will be passed to the server as nvarchar. This would cause significant performance issue as the data layer must perform data translation. You should stick with the approach of SqlParameter and specify the data types. – akd May 23 '17 at 09:23
  • I am not able to pass more than 3 paramters. If I do, i get following error "Additional information: The parameterized query '(@param1 type1,@param2 type2,@param3' expects the parameter '@param4', which was not supplied." Any suggestion? – sansy May 16 '18 at 12:59
  • So ExecuteSqlCommand is just a glorified String.Format call that also executes the sql command it formats. – georgiaboy82 Aug 31 '18 at 16:17
  • 1
    @GregBiles and others: This overload is _definitely NOT_ using string formatting, even though it looks like it. If you look at the sql in profiler, it is parameterized. .NET is doing the right thing in this case and changing {0} to `@p0, then parameterizing. It's a trade-off. It does look like evil string.format, but it is more concise (and db agnostic, see other answer below) than having to do new SqlParameter("@foo",paramvalue) – Daniel Oct 15 '19 at 14:54
74

You can either:

1) Pass raw arguments and use the {0} syntax. E.g:

DbContext.Database.SqlQuery("StoredProcedureName {0}", paramName);

2) Pass DbParameter subclass arguments and use @ParamName syntax.

DbContext.Database.SqlQuery("StoredProcedureName @ParamName", 
                                   new SqlParameter("@ParamName", paramValue);

If you use the first syntax, EF will actually wrap your arguments with DbParamater classes, assign them names, and replace {0} with the generated parameter name.

The first syntax if preferred because you don't need to use a factory or know what type of DbParamaters to create (SqlParameter, OracleParamter, etc.).

Ken Smith
  • 20,305
  • 15
  • 100
  • 147
Will Brown
  • 856
  • 6
  • 4
  • 7
    Upvoted for mentioning that the {0} syntax is database agnostic. "... you dont[sic] need to use a factory or know what type of DbParamaters[sic] to create ..." – Makotosan Mar 17 '15 at 18:51
  • Scenario 1 is deprecated in favour of an interpolated version. Equivalent is now: DbContext.Database.ExecuteSqlInterpolated($"StoredProcedureName {paramName}"); – ScottB Jan 26 '20 at 21:29
23

The other answers don't work when using Oracle. You need to use : instead of @.

var sql = "Update [User] SET FirstName = :FirstName WHERE Id = :Id";

context.Database.ExecuteSqlCommand(
   sql,
   new OracleParameter(":FirstName", firstName), 
   new OracleParameter(":Id", id));
jessegavin
  • 74,067
  • 28
  • 136
  • 164
Ryan M
  • 1,721
  • 2
  • 15
  • 22
20

Try this (edited):

ctx.Database.ExecuteSqlCommand(sql, new SqlParameter("FirstName", firstName), 
                                    new SqlParameter("Id", id));

Previous idea was wrong.

Peter
  • 2,654
  • 2
  • 33
  • 44
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
17

For entity Framework Core 2.0 or above, the correct way to do this is:

var firstName = "John";
var id = 12;
ctx.Database.ExecuteSqlCommand($"Update [User] SET FirstName = {firstName} WHERE Id = {id}");

Note that Entity Framework will produce the two parameters for you, so you are protected from Sql Injection.

Also note that it is NOT:

var firstName = "John";
var id = 12;
var sql = $"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
ctx.Database.ExecuteSqlCommand(sql);

because this does NOT protect you from Sql Injection, and no parameters are produced.

See this for more.

Greg Gum
  • 33,478
  • 39
  • 162
  • 233
  • 3
    I love .NET Core 2.0 so much it gives me tears of joy :') – Joshua Kemmerer Jan 07 '19 at 23:09
  • I can see some people's enthusiasm since .NET Core 2.0 has been a smoother ride for me so far. – Paul Carlton Jul 08 '19 at 13:54
  • How is the first one not vulnerable to SQL injection? Both use C# string interpolation. The first one wouldn't be able to preempt the string expansion, as far as I know. I suspect you meant that to be `ctx.Database.ExecuteSqlCommand("Update [User] SET FirstName = {firstName} WHERE Id = {id}", firstName, id);` – CodeNaked Aug 20 '19 at 14:14
  • @CodeNaked, No, I did not mean that. EF is aware of the issue, and creates two actual parameters to protect you. So it is not just a string that is passed through. See link above for details. If you try it in VS, my version will not issue a warning about Sql Injection, and the other one will. – Greg Gum Aug 21 '19 at 11:10
  • 2
    @GregGum - TIL about `FormattableString`. You're right and that's pretty cool! – CodeNaked Aug 22 '19 at 13:37
  • In the first example I think a parenthesis is missing – Flatlineato Jan 21 '22 at 11:35
  • 1
    @Flatlineato, Parenthesis added. – Greg Gum Jan 21 '22 at 13:40
4

Simplified version for Oracle. If you don't want to create OracleParameter

var sql = "Update [User] SET FirstName = :p0 WHERE Id = :p1";
context.Database.ExecuteSqlCommand(sql, firstName, id);
Andreas
  • 659
  • 6
  • 17
4
var firstName = "John";
var id = 12;

ctx.Database.ExecuteSqlCommand(@"Update [User] SET FirstName = {0} WHERE Id = {1}"
, new object[]{ firstName, id });

This is so simple !!!

Image for knowing parameter reference

enter image description here

Roy Scheffers
  • 3,832
  • 11
  • 31
  • 36
zaw
  • 109
  • 1
  • 5
3

For the async Method ("ExecuteSqlCommandAsync") you can use it like this:

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)
    });
Markus
  • 3,871
  • 3
  • 23
  • 26
  • This is not db-agnostic, it will only work for MS-SQL Server. It will fail for Oracle or PG. – ANeves Jun 26 '18 at 15:45
1

If your underlying database data types are varchar then you should stick with the approach below. Otherwise the query would have a huge performance impact.

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);

You can check Sql profiler to see the difference.

ANeves
  • 6,219
  • 3
  • 39
  • 63
akd
  • 6,538
  • 16
  • 70
  • 112
0
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();
Neco
  • 539
  • 4
  • 10
  • 7
    Any chance you could explain this code and why it is an answer to the question posed, so that those who come and find this later can understand it? – Andrew Barber Sep 25 '12 at 04:38
  • 1
    Problem with {0} syntax that you loose readability - i personally do not really like it. Problem with passing SqlParameters that you need to specify concrete implementation of DbParameter (SqlParameter, OracleParameter etc.). The provided example allows you to avoid these issues. – Neco Oct 11 '13 at 18:22
  • 3
    I guess that's a valid opinion, but you have not answered the question *actually being asked* here; How to pass parameters to `ExecuteSqlCommand()` You should be sure to answer the specific question being asked when you post answers. – Andrew Barber Oct 11 '13 at 21:17
  • 3
    Downvoted because it is unnecessarily complicated (e.g. uses reflection, re-inventing the wheel, fails to account for different database providers) – an phu Mar 20 '15 at 19:55
  • Up-voted because it shows one of the possible solutions. I'm sure ExecuteSqlCommand accepts parameters the same way SqlQuery does.I also like Dapper.net style of passing the parameters. – Zar Shardan Jun 29 '16 at 11:49
0

Multiple params in a stored procedure that has multiple params in vb:

Dim result= db.Database.ExecuteSqlCommand("StoredProcedureName @a,@b,@c,@d,@e", a, b, c, d, e)
Dani
  • 1,825
  • 2
  • 15
  • 29
0

Stored procedures can be executed like below

 string cmd = Constants.StoredProcs.usp_AddRoles.ToString() + " @userId, @roleIdList";
                        int result = db.Database
                                       .ExecuteSqlCommand
                                       (
                                          cmd,
                                           new SqlParameter("@userId", userId),
                                           new SqlParameter("@roleIdList", roleId)
                                       );
-4

For .NET Core 2.2, you can use FormattableString for dynamic SQL.

//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);
Circuit Breaker
  • 3,298
  • 4
  • 17
  • 19