EF Core FromSqlRaw with column variable - c#

Attempting to set the column as a parameter in FromRawSql. Query always returns null, I understand this is because the SQL is compiled before parameter values are set. How can I pass the column name into the query as a parameter?
What I'm trying:
var param = new SqlParameter[] {
new SqlParameter("#col", dataDiscriminator),
new SqlParameter("#value", itemDiscValue)
};
var thisq = context.Devices.FromSqlRaw("SELECT * FROM Devices WHERE #col = #value", param);
var thisDevice = thisq.SingleOrDefault();
Will produce the SQL:
DECLARE #col nvarchar(4) = N'Name';
DECLARE #value nvarchar(26) = N'Registration Template';
SELECT * FROM Devices WHERE #prop = #value

you can not use parameter for table or column name. Try this
var param = new SqlParameter[] {
new SqlParameter("#value", itemDiscValue)
};
var thisq = context.Devices.FromSqlRaw($"SELECT * from Devices
WHERE [{dataDiscriminator}] = #value", param).ToList();
var thisDevice = thisq.SingleOrDefault();
I don' t know where dataDiscriminator data from but always remember about the sql script injections.

Related

SQL where clause with multiple “OR” conditions dynamically

I have a c# windows application where I get data from SQL database.
I need to write an sql query with where clause having multiple “OR” conditions dynamically.
Select * from table
where
(Name = #name and Account = #account)
OR
(Name = #name and Account = #account)
OR
(Name = #name and Account = #account)
OR
(Name = #name and Account = #account)
OR
……..
Here number of OR statement can vary based on the number of rows in the data table.
How can write a sql query that can use OR statements dynamically?
E.g.
var names = new[] {"name1", "name2", "name3"};
var accounts = new[] {"account1", "account2", "account3"};
var conditions = new List<string>();
var command = new SqlCommand();
for (var i = 0; i < names.Length; i++)
{
conditions.Add($"(Name = #Name{i} AND Account = #Account{i})");
command.Parameters.Add($"#Name{i}", SqlDbType.VarChar, 50).Value = names[i];
command.Parameters.Add($"#Account{i}", SqlDbType.VarChar, 50).Value = accounts[i];
}
command.CommandText = $"SELECT * FROM MyTable WHERE {string.Join(" OR ", conditions)}";
This still uses parameters so it still avoids the possibility of SQL injection but it also allows you to build the query dynamically.

Return scalar value from parameterized SQL statement using DB2 EF Core

In a class library targeting .NET 5.0, EntityFramework Core, I can use the following code to return a scalar value from parameterized SQL statement using EF Core for SQL Server:
public object GetResult()
{
var context = _contextFactory.CreateDbContext();
var commandText = "SELECT #p0 = (SELECT Case When Exists(SELECT null WHERE #p1 != 'B') THEN 1 Else 0 End)";
var p0 = new SqlParameter
{
ParameterName = "#p0",
SqlDbType = SqlDbType.Bit,
Direction = ParameterDirection.Output,
};
var p1 = new SqlParameter
{
ParameterName = "#p1",
SqlDbType = SqlDbType.VarChar,
Size = 1,
Direction = ParameterDirection.Input,
Value = 'A'
};
_ = context.Database.ExecuteSqlRaw(commandText, new[] { p0, p1 });
return p0.Value;
}
I would like to be able to do something similar using EF Core for DB2.
I am using the IBM.EntityFrameworkCore 5.0.0.300 Nuget package. The DB2 EF Core provider does not support named parameters, so must replace parameter names with ? markers in command string...however, this statement is not supported on DB2 and throws an error.
public object GetResult()
{
var context = _contextFactory.CreateDbContext();
var commandText = "SELECT ? = (SELECT Case When Exists(SELECT 1 FROM sysibm.sysdummy1 WHERE ? != 'R') THEN 1 Else 0 End FROM sysibm.sysdummy1)";
var p0 = new DB2Parameter
{
ParameterName = "#p0",
SqlDbType = DB2Type.Boolean,
Direction = ParameterDirection.Output,
};
var p1 = new DB2Parameter
{
ParameterName = "#p1",
SqlDbType = DB2Type.VarChar,
Size = 1,
Direction = ParameterDirection.Input,
Value = 'A'
};
_ = context.Database.ExecuteSqlRaw(commandText, new[] { p0, p1 });
return p0.Value;
}
I could use the context to get the ConnectionString and create a new DB2Connection and a parameterized DB2Command and call ExecuteScalar to get the result, but would like to try to return value as a strongly typed parameter rather than the ExecuteScalar which returns the value as an object.
Does anyone know how I may craft a SQL statement for DB2 to return a scalar value as an output or result parameter using EF Core and ExecuteSqlRaw??
could use the context to get the ConnectionString and create a new DB2Connection
There's no need to do that. To use ADO.NET directly with your DbContext, just do this:
var con = context.Database.GetDbConnection();
con.Open();
. . .

EF Core Execute SQL with parameters

The app is .Net Core 3.1, using EF Core 3 and a SQL Server on Azure
So I'm trying to create a table in my database with data from the client and I want to be safe from SQL injection.
So far I've tried with using a FormattableString which according to the doc is safe against SQL injection:
public Task CreateTableAsync(string tableName, JSchema tableSchema)
{
return TransactionAsync(async () =>
{
// Get the fields for the table creation
var fields = await ParseJSchemaForCreationAsync(tableSchema);
var sql = "CREATE TABLE {0} (";
var sqlParams = new List<object>
{
tableName
};
var first = true;
var count = 1;
foreach (var entry in fields)
{
// entry.Value is from code so it's safe againt injection
sql += first ? $"{{{count}}} {entry.Value}" : $", {{{count}}} {entry.Value}";
first = false;
sqlParams.Add(entry.Key);
count++;
}
sql += ");";
var safe = FormattableStringFactory.Create(sql, sqlParams.ToArray());
// Create the table
await _dbContext.Database.ExecuteSqlInterpolatedAsync(safe);
});
}
But I've an error : "incorrect syntax near '#p0'", despite it seems to generate a valid query (when getting the value of sage I got :
"CREATE TABLE sqlDataSourceGrainTest (Id uniqueidentifier NOT NULL PRIMARY KEY, CreatedAt datetime2(0), UpdatedAt datetimeoffset(3), FirstName nvarchar(4000), Birthdate date, XId uniqueidentifier, Datetime datetime2(0), Timestamp timestamp, Height decimal(18, 2), HasFoodAllergy bit, Age bigint);"
I've also tried to use with SQLParameter (which I prefer):
public Task CreateTableAsync(string tableName, JSchema tableSchema)
{
return TransactionAsync(async () =>
{
// Get the fields for the table creation
var fields = await ParseJSchemaForCreationAsync(tableSchema);
var sql = "CREATE TABLE #tableName (";
var sqlParams = new List<SqlParameter>()
{
new SqlParameter
{
ParameterName = "tableName",
Value = tableName,
}
};
var first = true;
foreach (var entry in fields)
{
sql += first ? $"#{entry.Key} {entry.Value}" : $", #{entry.Key} {entry.Value}";
first = false;
var sqlParam = new SqlParameter
{
ParameterName = $"{entry.Key}",
Value = entry.Key
};
sqlParams.Add(sqlParam);
}
sql += ");";
// Create the table
await _dbContext.Database.ExecuteSqlRawAsync(sql, sqlParams);
});
}
But I've have the error : "Incorrect syntax near '#tableName'."
Can someone help me to find the correct way to create the table? Is there any rules that say we can't use sql with parameters to create the table.
I've will also need to made update of the table, insert records and update records
Thanks
Edit: Based on answers from DavidG and HoneyBadger I've tried:
public Task CreateTableAsync(string tableName, JSchema tableSchema)
{
return TransactionAsync(async () =>
{
// Get the fields for the table creation
var fields = await ParseJSchemaForCreationAsync(tableSchema);
var sql = $"CREATE TABLE {tableName} (";
var sqlParams = new List<SqlParameter>();
var first = true;
foreach (var entry in fields)
{
sql += first ? $"#{entry.Key} {entry.Value}" : $", #{entry.Key} {entry.Value}";
first = false;
var sqlParam = new SqlParameter
{
ParameterName = $"{entry.Key}",
Value = entry.Key
};
sqlParams.Add(sqlParam);
}
sql += ");";
// Create the table
await _dbContext.Database.ExecuteSqlRawAsync(sql, sqlParams);
});
}
But now the error is "Incorrect syntax near '#id'" which is the name of the first parameter
SQL I see: CREATE TABLE tableTests ( #Id uniqueidentifier NOT NULL PRIMARY KEY, #CreatedAt datetime2(0), #UpdatedAt datetimeoffset(3), #FirstName nvarchar(4000), #Birthdate date, #XId uniqueidentifier, #Datetime datetime2(0), #Timestamp timestamp, #Height decimal(18, 2), #HasFoodAllergy bit, #Age bigint);"
Can't I use any parameters at all in the creation of a table?
Object names can't be parameters, so you'll need to use concatenation:
var sql = "CREATE TABLE " + tableName + " (";
I hope your users aren't involved in deciding the name of the table, so sql injection shouldn't be an issue.

Stored procedure doesn't return any data when being called from the C# code

I have a stored procedure that accepts around 8 parameters and returns a bunch of data. The stored procedure itself runs fine and it returns the data when I call it in SSMS. But when I call it from my application using Dapper ORM, I don't get any data back.
I looked up similar threads here and I've tried everything that was suggested like this one: Dapper multi-parameter stored procedure query returns nothing back from database
Here's my code:
localConnection = new SqlConnection(defaultSettings.SetConnectionString(auth.DB_Name));
var parameters = new DynamicParameters();
try
{
parameters.Add("#param2", "val1");
parameters.Add("#param3", "val2");
parameters.Add("#param4", "val3");
parameters.Add("#param5", "val4");
parameters.Add("#param6", "val5");
parameters.Add("#param7", "val6");
parameters.Add("#param8", "val7");
var orders = localConnection.Query<Order>("spGetData", parameters, commandType: CommandType.StoredProcedure).ToList();
}
catch (Exception ex)
{
log.Error("Getdata Error: " + ex.Message);
}
Here's the SQL Server stored procedure:
CREATE PROCEDURE [dbo].[spGetData]
#param1 VARCHAR(8),
#param2 VARCHAR(8),
#param3 VARCHAR(8),
#param4 VARCHAR(8),
#param5 VARCHAR(8),
#param6 VARCHAR(8),
#param7 VARCHAR(8)
AS
SET NOCOUNT ON
SELECT TOP 1 *
FROM Orders a
JOIN ChangeLog b ON a.id = b.id
WHERE a.name = #param1
AND a.col2 = #param2
AND b.col1 = #param3
AND b.col3 = #param4
AND b.col4 = #param5
AND a.col3 = #param6
AND b.col5 = #param7
RETURN 0
Any idea what is going on? Or what is it that I'm doing wrong?
There's no problem with Dapper and multiple parameters as far as I can see. Here's a test code I created:
class Program
{
static void Main(string[] args)
{
SqlConnection c = new SqlConnection(#"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=tempdb;Data Source=.\SQLEXPRESS");
c.Execute(#"
CREATE PROCEDURE GetPerson(
#N VARCHAR(10),
#A INT,
#S INT
)AS
BEGIN
SELECT #N as Name, #A as Age, #S as Salary;
END;");
//works
var p = c.Query<Person>("GetPerson", new { A = 1, N = "John", S = 1000 }, commandType: System.Data.CommandType.StoredProcedure);
//doesn't work, "procedure expects parameter #A which was not supplied"
int i = 2, j = 2000; string n = "Frank";
var q = c.Query<Person>("GetPerson", new { i, n, j }, commandType: System.Data.CommandType.StoredProcedure);
//works
int A = 3, S = 3000; string N = "Joe";
var r = c.Query<Person>("GetPerson", new { S, A, N }, commandType: System.Data.CommandType.StoredProcedure);
//works
DynamicParameters dp = new DynamicParameters();
dp.Add("#A", 4);
dp.Add("#N", "Derek");
dp.Add("#S", 4000);
var s = c.Query<Person>("GetPerson", dp, commandType: System.Data.CommandType.StoredProcedure);
DynamicParameters dp2 = new DynamicParameters();
dp2.Add("A", 5);
dp2.Add("N", "Tim");
dp2.Add("S", 5000);
var t = c.Query<Person>("GetPerson", dp2, commandType: System.Data.CommandType.StoredProcedure);
}
}
class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
All the queries that //works return a list of 1 person. The Salary column output by the query is not represented in the Person class so it just gets lost.
Key takeaways from this:
dapper is flexible about how it accepts parameters
dapper parameter name is important
dapper doesn't care about # in a parameter name
query columns that have no matching property in the strong class are not represented
And as you identified it's the last one that was critical in your case; your query wasn't outputting columns with names that matched your orders object
I found the issue. The Order class I'm using has properties that should match the stored procedure's data columns, which it was not.
Once I fixed that, I got it working.

Passing conditional parameters to Database.SqlQuery

I have been doing the following to pass parameters with my sql query:
var retval = context.MyObject.SqlQuery(sql.ToString(),
new SqlParameter("#ProductId", productId),
new SqlParameter("#CustomerId", customerId)).ToList();
I ran into a scenario where #CustomerId will not always be used in my sql string. Meaning I used an if statement to either add (Where p.CustomerId = #CustomerId) or keep it out.
I realized that I can't pass my parameters in directly to the SqlQuery function now. Instead I have to create an object list of parameters and then pass it in:
SqlParameter param1 = new SqlParameter("#ProductId", productId);
SqlParameter param2 = new SqlParameter("#CustomerId", customerId);
object[] parameters = new object[] { param1, param2 };
How can I make it so I can use an if statement to either add the CustomerId parameter to my parameters array or not?
You can edit your query to this
SELECT * FROM Product p
WHERE
p.ProductId = #ProductId
AND (#CustomerId IS NULL OR p.CustomerId = #CustomerId)
Then you pass DBNull.Value to #CustomerId if it is not used
You can try using a sql command instead as it allows using collections of parameters. But this might require you to change your code structure a bit.
int a = 1;
SqlCommand sql = new SqlCommand();
sql.CommandText = "SELECT * FROm somwhere";
List<SqlParameter> lstParams = new List<SqlParameter>();
if (a == 1)
{`enter code here`
SqlParameter sqlParam1 = new SqlParameter();
lstParams.Add(sqlParam1);
}
else if (a == 2)
{
SqlParameter sqlParam2 = new SqlParameter();
lstParams.Add(sqlParam2);
}
sql.Parameters.AddRange(lstParams.ToArray());
sql.BeginExecuteReader();

Categories

Resources