I am using c# + EF6 running into an issue when trying to parameterize a SQL command. In rare cases I have to use the Context.Database.ExecuteSqlCommand(sqltext).
The issue is this:
the query that is executed is using an IN keyword e.g.
DELETE FROM MyTable WHERE ID in (#ids)
#ids is a list of integers - if there is a single int then the query runs ok due to implicit conversion:
exec sp_executesql N'DELETE FROM MyTable WHERE ID IN (#ids)',N'#ids nvarchar(7)',#ids=N'1'
but when there is a list of items then it will fail with a conversion error
exec sp_executesql N'DELETE FROM MyTable WHERE ID IN (#ids)',N'#ids nvarchar(7)',#ids=N'1,2'
Conversion failed when converting the nvarchar value '1,2' to data type int.
How can I structure the parameter or sq to work using sql parameters?
thanks in advance...
Related
I need to add the schema value dynamically to the query. I was trying to construct it the way we usually use values but realised it does not work with the schema names the same way.
This was what I was trying to do
sql = "SELECT Name FROM [#dbo].[Members]";
...
command.Parameters.Add("#dbo", SqlDbType.VarChar).Value = "dbo";
I know I can construct the query by directly adding the variable in the query like this:
sql = $#"SELECT Name FROM [{parameter}].[Members]";
But I wanted to disallow any kind of SQL Injection so want to go ahead using the parameterized query as above.
Can anyone help with a possible idea to implement this?
TIA
The schema is not a parameter. Query parameters are equivalent to function parameters in eg C#. They're used to pass values. In SQL, the table and columns are equivalent to C# types and properties. You can't specify them by name. The schema in a SQL query is similar to the Namespace in C#. The table is equivalent to a Type. In C#, just because Sales.Record and Diagnostics.Record have the same type name doesn't mean the two types can be used the same way.
The question doesn't explain why the schema name is passed dynamically. It's almost certain there are easier, more efficient and safer ways to query similar tables in multiple schemas, but the solution would depend on the actual problem.
There are some techniques that can be used to make such a dynamic query safe if not efficient. I'd really, really try to avoid treating the schema as a value though.
Using QUOTENAME
One option, is to use QUOTENAME in a T-SQL script to construct a dynamic query. At least this way a syntax error will be thrown if the schema and table names are wrong:
sql = #"declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
select #sql;
exec sp_executesql #sql;";
...
command.Parameters.Add("#dbo", SqlDbType.NVarChar,100).Value = "dbo";
QUOTENAME will convert something like sys].schemas; PRINT ''x''; -- to [[sys]].schemas; PRINT 'x'; --]. This will result in an error :
declare #sql nvarchar(max)= 'select * from [' +quotename('sys].schemas; PRINT ''x''; --')
select #sql
exec sp_executesql #sql;
--------
select * from [[sys]].schemas; PRINT 'x'; --]
Invalid object name '[sys].schemas; PRINT 'x'; --'.
It's too easy to make quoting mistakes with such scripts. This could be extracted into a stored procedure :
CREATE PROCEDURE GetMemberNameBySchema
#dbo nvarchar(100)
as
declare #sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(#dbo) + '.[Members]';
exec sp_executesql #sql;
Verify the Schema name
Query sys.schema to ensue the schema is correct before constructing the query. Let's say you're using Dapper (so I don't have to write all the ADO.NET code) :
var schema="dbo";
var isValid=connection.ExecuteScalar<bool?>(
"select 1 from sys.schema where name=#name",
new {name=schema});
//isValid will be null if nothing is found
if(isValid ==true)
{
var names=connection.Query($"SELECT Name FROM [{schema}].[Members]");
...
}
This is safe to do because the first query ensured the schema name is valid.
You can only pass parameters to dynamic query not object names as far as I know. You can do something like below:
Update after seeing Panagiotis' solution. we can take out the exists part outside. I am no expert in C# but something similar we want to do:
DECLARE #Table NVARCHAR(100);
DECLARE #Schema NVARCHAR(50);
SET #Schema = 'dbo'; --This part will come from C#
SET #Table = 'Tablename'; --This part will come from C#
DECLARE #sql NVARCHAR(MAX);
IF EXISTS(SELECT * FROM sys.tables WHERE name = #Table and schema_id = SCHEMA_ID(#Schema))
BEGIN
SET #sql = N'SELECT TOP 1 * FROM '+ QUOTENAME(#Schema) + '.' + QUOTENAME(#Table) +';';
PRINT #sql;
EXEC sp_executesql #sql;
END
ELSE
RAISERROR('Table or Schema doesn''t exist.',16,1);
In the IF EXISTS you need to pass the table name and schema name as parameter to dynamic query.
In the actual query you need to make a concatenation of table name and schema name.
This will prevent sql injection as well and if the table is not there then raise an error.
I'm using EF 6.4.4 to query a SQL view. Now the view is not really performing optimal, but i don't control it.
I'm executing the following code with a WHERE statement on a string/nvarchar property
_context.ViewObject
.Where(x => x.Name == nameFilter)
.ToList();
Similarly, i have the same SQL statement executed in SMSS
SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = '<nameFilter>'
My problem is that the EF variant is way slower than the direct SQL query.
When checking out the SQL query generated by EF i see the following:
SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = #p__linq__0
with parameter #p__linq__0 is of type NVARCHAR(4000) NULL
This even though that my input variable is not NULL and has a lenght of maximum 6 characters.
When i execute the same sql query with this parameter, it is slow in SMSS as well.
Apparently, this has somethign
So what i want to do is alter the SQL query parameter that EF is using to generate this query. This to make sure that my parameter is more accurately represented in the query and that i can get the same performance as directly in SMSS.
Is there a way to do this?
Whats going on: parameter sniffing
Execute the following in SSMS and you will propably see the same performance.
EXECUTE sp_executesql N'SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = #nameFilter'
,N'#nameFilter nvarchar(4000)'
,#nameFilter = '<namefilter>';
sp_executeSql is used by EF to execute queries against a database and thus, when you write .Where(x => x.Name == nameFilter) this is translated to the above statement.
Making you suffer from parameter sniffing.
You could fix this by adding recompile to your queries like described here But be aware that adding recompile to all queries might have negative impact on the other queries.
You can execute the following queries with actual execution plan to see the difference:
Query with WHERE Name = #NameFilter
Query with WHERE Name = '<NameFilter>'
Query with WHERE Name = #NameFilter OPTION(RECOMPILE)
If it's not parameter sniffing, it might be implicit conversions, but I'm guessing both types are NVARCHAR so this shouldn't matter.
99% of the time it's parameter sniffing.
I've table in SQL server 2008 r2 Like
Create Table Table1
(
Col1 varchar(100)
)
Where I've records for Col1 as value are given below :
1. select count(*) from Table2;
2. select getDate();
I'm using this table in my app using Linq to SQl in .dbml
How can I execute these queries and get returned value from that query in loop(1 by 1) using Linq? when I don't have exact return type of coming value. I tried ExecuteQuery() function but it needs type at compile time and my o/p can be any type as per query or function used in query.
Im trying to insert something in persian into my Database (SQL server 2008) from C# code.
The problem is when u insert in sql server you just simply use N'چیزی' for utf-8.But how
Can u do that in LINQ ? (I dont wanna use stored procedures).
Thnks
Simply use the string "چیزی" and make sure your .cs files are UTF-8 too.
Use this as the parameter in a parameterized query or in whatever ORM you are using. The data access layer will take care of encoding.
LINQ to EF or LINQ to SQL will generate/use that N'' automatically everywhere:
exec sp_executesql N'update [dbo].[Users]
set [Name] = #0
where (([Id] = #1) and ([Name] = #2))
',N'#0 nvarchar(max) ,#1 int,#2 nvarchar(max) ',#0=N'User name 1',#1=1,#2=N'Vahid'
Those N'ي' (Arabic Ye) or N'ى' (Persian Ye) both are the valid UTF-8 characters. So you need to convert between them yourself (before inserting the data) and it's not the duty of EF or SQL Server.
Some examples about it (in Persian).
I want to use LINQ to call stored procedure in this way but stored procedure I want to call contain SQL string that executed by
EXEC sp_executesql #strSQL
In this way Visual Studio does not generate result class. To resolve this problem I execute SQL string by
INSERT INTO #Temp EXEC sp_executesql #strSQL
SELECT * from #Temp
Maybe it is not the best way but it works. The problem is that it sometimes return items in wrong sequence. How to fix it?
Use an Order By to specify the sort order