Dapper can't recognize wildcard param in the single quotes - c#

I am trying to get indexes fragmentation info from database.
Here the dapper sql query:
var result = await _dbConnection.QueryAsync<IndexFragmentationModel>($#"
select
a.index_id as Id, name as Name, avg_fragmentation_in_percent as FragmentationPercent
from sys.dm_db_index_physical_stats (DB_ID(N'#dbName'), OBJECT_ID(N'#tableName'), null, null, null) as a
join sys.indexes as b on a.object_id = b.object_id and a.index_id = b.index_id;
", new
{
dbName = dbName,
tableName = tableName
});
return result.ToList();
Parameters are not passing the the places where they are expected.
Could anybody please suggest - maybe there is another way to pass them ?

You're using the literal strings "#dbName" and "#tableName", not the parameters' values.
Remove the N' and ' that surround them.

Related

Passing an array to SQL parameter with Dapper

I want to insert into a table an array of values if they don't exist, the array is small and would not exceed 10 items, so it is safe to pass in an insert.
How can I execute this code with Dapper? I tried the following but I am getting an error:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT NewNames.Name FROM (VALUES(#Names)) AS NewNames (Name)
WHERE NOT EXISTS (SELECT 1 FROM Country AS C WHERE C.Name = NewNames.Name);";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, new { Names = countries.Select(x => x.Name) });
The error is:
Core Microsoft SqlClient Data Provider: Incorrect syntax near ','.
There is a similar problem on SO, but it is for the IN clause:
dapper "IN" clause not working with multiple values
Is there another way in Dapper to pass an array in my case?
What you are trying to do isn't possible. But instead of the array, you can just use your countries collection. It has a Name property and Dapper will run the query for each item in a suitable collection:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT #Name WHERE NOT EXISTS
(
SELECT 1
FROM Country
WHERE Name = #Name
)";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, countries);

dapper query - fix for "Unclosed quotation mark near ..." error

This query works; that is, it returns the expected results:
var r = sql.Query<T>("select * from TableName where Name = '" + name + "'");
but, if one of the 'names' values contains an apostrophy (which is true), then an exception is thrown '{"Incorrect syntax near 'Resources'.\r\nUnclosed quotation mark after the character string ''."}' - in an attempt to fix that problem, my query no longer returns any results; but it should.
I've attempted to change the code in a few ways, but no results are returned with either of the following changes:
var r = sql.Query<T>("select * from TableName where Name = '#name'", new { name });
or
var args = new DynamicParameters(name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.AddDynamicParams(new { name });
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.Add("#name", name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
This is probably something trivial that I have simply just not yet grasped the concept for ... but I'm at the point of having spent too much time trying to figure it out - hence the question.
Using a parameter is the right way to go. You absolutely don't want to put the value into the query itself as you did in your first snippet. However, you've put the #name in quotes, which means it's being treated as a string literal... it's looking for a name value of exactly #name rather than the value of the #name parameter. You want:
var r = sql.Query<T>("select * from TableName where Name = #name", new { name });
(That's probably the simplest way of passing the parameters, though the other approaches should work too.)
Now I haven't actually used Dapper myself, but that's what I'd expect...

SELECT string[] array of columns FROM Table

What I want to achieve is, from this:
string[] QueryString = new string[]{
"Column1",
"Column2"
};
Create this:
SELECT Column1,Column2
FROM SomeTable
I know that I could do a concat but that's not a very clean way to doit.
I know that I could do a concat but that's not a very clean way to do it.
Seems pretty clean to me:
string sql = "SELECT " + string.Join(", ", QueryString) + " FROM SomeTable";
Although you're susceptible to SQL injection attacks:
string[] QueryString = new string[]{
" * FROM SomeTable; DROP TABLE SomeTable; --"
};
So ONLY do this if you have complete control of the column names that can be used (e.g. populated from the table metadata).
Declare #qtext varchar(max) = 'select '+column1+ ' , '+column2+ ' from sometable '
Exec(#qtext)
You could use String.Format
http://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx
String query =
String.Format("SELECT {0}, {1} FROM SomeTable", QueryString[0], QueryString[1]);
Although, as always when constructing sql queries, be aware of sql injection attacks.

Passing parameter to SQL select statement IN clause acts weird.

I've got the following query that returns 2 records (in DataSet's query builder)
SELECT EmpID, Name, id
FROM Users
WHERE (CAST(id AS Varchar(20)) IN ('5688','5689'))
Now if I do the same query passing the parameter instead from code behind: String param = "'5688','5689'"; it returns null.
WHERE (CAST(id AS Varchar(20)) IN (#param))
I tried taking off the very first and last ', but that did not make a diffrence.
!!!id is a unique PK!!!
Anyone's got a clue?
The solution I found is quite simple, this works like a charm and there's no need for sps or other functions;
SQL:
SELECT whatever
FROM whatever
WHERE (PATINDEX('%''' + CAST(id AS Varchar(20)) + '''%', #param) > 0)
C#:
String param = "'''1234'',''4567'''";
dataTable1 = tableAdapter1.getYourValues(param);
A variable is not allowed in the IN clause.
You are expecting the values as a comma delimited string you could use the split function (user defined and non-standard) to join them with the original tables:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=326300&SiteID=1
For more information you can visit this
('5688','5689') is an array of values.
Defining String param = "'5688','5689'"; and using it as (#param) makes ('5688','5689') a string. Which wont work.
Bibhas is correct. For me this worked:
string param="'1234','4567'"; we can't use param as SQL Parameter(#param).
command = new SqlCommand("SELECT * FROM table WHERE number IN (" + param + ")", connection);
command.ExcecuteReader();

Using SqlParameter to create Order By clause

I am trying to move all of my references to variables in SQL statements to the SqlParameter class however for some reason this query fails.
string orderBy = Request.QueryString["OrderBy"];
//Fix up the get vars
if (orderBy == null)
orderBy = "name ASC";
string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY #OrderBy";
SqlCommand cmd = new SqlCommand(selectCommand, dataConnection);
cmd.Parameters.Add(new SqlParameter("#OrderBy", orderBy));
//Create the SQLDataAdapter instance
SqlDataAdapter dataCommand = new SqlDataAdapter(cmd);
//Create the DataSet instance
DataSet ds = new DataSet();
//Get data from a server and fill the DataSet
dataCommand.Fill(ds);
Here is the error
System.Data.SqlClient.SqlException: The SELECT item identified by the ORDER BY number 1 contains a variable as part of the expression identifying a column position. Variables are only allowed when ordering by an expression referencing a column name.
It fails on this line.
dataCommand.Fill(ds);
You really have three options.
1) Use a dataview to order the result set
2) If you know the columns that can be ordered you can test for the string and then use then select the order. e.g.
For example this will work
DECLARE #orderby varchar(255)
SET #orderby = 'Name ASC'
SELECT [Your Column here ]FROM sys.tables
ORDER BY
case WHEN #orderby = 'Name ASC' Then name ELSE null END ASC,
case WHEN #orderby = 'Name DESC' Then name ELSE null END DESC,
CASE WHEN #orderby = 'Object_id ASC' then object_id ELSE null END ASC,
CASE WHEN #orderby = 'Object_id DESC' then object_id ELSE null END DESC
3) The final option is to do the same as #2 but in your C# code. Just be sure you don't just tack on the ORDER BY clause from user input because that will be vunerable to SQL injection.
This is safe because the OrderBy Url parameter "Name Desc; DROP table Users"will simply be ignored
string SafeOrderBy = "";
string orderBy = Request.QueryString["OrderBy"];
//Fix up the get vars
if (orderBy == null)
orderBy = "name ASC";
if (orderby == "name Desc")
{
SafeOrderBy == "name Desc"
}
string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY "
selectCommand += SafeOrderBy ;
Using SqlCommand is the way to prevent from sql injection. Your way of changing of the order by is the same as using sql injection in this context so it shouldnt be allowed - params are used as the constants, can't be used as column or table names.
u dont have to concatenate content of sortBy just use it as enum and depending on its value concatenate something you're sure that is safe. Like this:
If(orderBy == "some_column")
{
selectColumn += "someColumn";
}
...
I ran in to the same problem as you, however the listed solutions could not be used since my possible sort column was one of the properties of a model and that would mean way too many if-statements if the model is big.
My solution to this related problem is to use Reflection. Something like:
class MyModel {
public string MyField1 { get; set; }
public string MyField2 { get; set; }
// ...
}
//...
using System.Reflection;
// sortBy = "MyField1"
// sortDirection = "Asc";
var sql = "SELECT FROM foo WHERE bar=baz ORDER BY ";
foreach (var prop in typeof(MyModel).GetProperties())
{
if (sortBy.Equals(prop.Name))
{
sql += (prop.Name + (sortDirection.Value.Equals("Asc") ? " ASC" : " DESC"));
break;
}
}
The benefit to this solution is that no matter how my model changes, this code will support sorting by any of its properties and thus doesn't need to be changed as well.
I found an example how to do this here
you can define different sort orders in a CASE-structure and execute them appropriately to your variable value:
SELECT CompanyName,
ContactName,
ContactTitle
FROM Customers
ORDER BY CASE WHEN #SortOrder = 1 THEN CompanyName
WHEN #SortOrder = 2 THEN ContactName
ELSE ContactTitle
I didn't test it myself but it could work. You can give it a try. An obvious disadvantage is that you have to code all the order-by statements.
You're just concatenating strings. A simpler approach would be:
string orderBy = "name ASC";
string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY " + orderBy;
I'm assuming you're doing this at all because you're letting the caller decide sort field/direction, hence orderBy separated.
Parameters, as the error message sort of obliquely hints at, would be used in a WHERE clause, e.g. WHERE someColumn = #someValue etc.

Categories

Resources