I am using the Select command to select particular data rows from a DataTable:
DataRow[] dr;
dr = dataTable.Select("Roll_No = '"+rNo+"'");
I am getting the correct answer, but what if I want to use a constant defined for Roll_No?
I have defined constants:
public const string ROLL_NUMBER = "Roll_No";
So what will be the code for DataTable.Select()?
If I'm understanding your correctly, just substitute the variable for the hard-coded value.
var rows = dataTable.Select(string.Format("{0} = '{1}'", ROLL_NUMBER, rNo));
Assuming Roll_No is a numeric field, those single parentheses in the Select statement might mess up your query... if you don't get the expected results, try removing them.
Alternatively, you've also got the option of using a LINQ statement:
var rows = dataTable.AsEnumerable()
.Where(x => x.Field<int>(ROLL_NUMBER) == rNo);
Update (from follow-up comment):
can I use like follows? var dr = datTable.Select("Constants.ROLL_NUMBER = '"+rNo+"'"); here , Constants.ROLL_NUMBER is constant defined for roll number.
No, because your variable name is inside of a quote and so you'll end up filtering on the string literal "ROLL_NUMBER", not the underlying column name stored in ROLL_NUMBER.
To use your code, you'd do it like this:
var dr = dataTable.Select(Constants.ROLL_NUMBER = "'" + rNo + "'");
A string is a string. You already know how to concatenate strings because you're already doing it. ROLL_NUMBER would just be another string, just like rNo in your current code.
That said, I would tend to use String.Format for that sort of expression.
dataTable.Select() Method can only pass strings. Just Like it
public DataRow[] Select(string filterExpression,string sort)
If You want to pass a constant than you have to convert it into string.
Related
I have a table with a lot of employees in it, every person has a Name column with their full name.
I then want to do a query similar to this when searching for people:
SELECT * FROM Employees WHERE Name LIKE '%' + #value1 + '%' AND Name LIKE '%' + #value2 +'%' AND so forth...
for an arbitrary array of values.
My Dapper code would look something like this:
public IEnumerable<Employee> Search(string[] words)
{
using var connection = CreateConnection();
connection.Query<Employee>("SELECT * etc.", words);
}
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
Caveat: I don't know how Dapper actually passes an array to the query, which limits my creative ideas for working around this :-D
And also: Changing the Table structure is, unfortunately, out of the question. And I'd rather avoid fetching every single person into .Net memory and doing the filtering there.
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
Because the set of where conditions is not fixed you will need to build the query dynamically. But that does not mean you cannot parameterise the query, you just build the parameter list alongside building the query. Each time a word from the list add to the condition and add a parameter.
As Dapper doesn't directly include anything that takes a collection of DbParameter, consider using ADO.NET to get an IDataReader and then Dappter's
IEnumerable<T> Parse<T>(this IDataReader reader)
for the mapping.
Such a builder would be very roughly
var n = 0;
for (criterion in cirteria) {
var cond = $"{crition.column} like #p{n}";
var p = new SqlPatameter($"#p{n}", $"%{crition.value}%";
conditions.Add(cond);
cmd.Parameters.Add(p);
}
var sql = "select whetever from table where " + String.Join(" and ", conditions);
cmd.CommandText = sql;
var reader = await cmd.ExecuteReaderAsync();
var res = reader.Parse<TResult>();
For performance reasons, it's much better to do this as a set-based operation.
You can pass through a datatable as a Table-Value Parameter, then join on that with LIKE as the condition. In this case you want all values to match, so you need a little bit of relational division.
First create your table type:
CREATE TYPE dbo.StringList AS TABLE (str varchar(100) NOT NULL);
Your SQL is as follows:
SELECT *
FROM Employees e
WHERE NOT EXISTS (SELECT 1
FROM #words w
WHERE e.Name NOT LIKE '%' + w.str + '%' ESCAPE '/' -- if you want to escape wildcards you need to add ESCAPE
);
Then you pass through the list as follows:
public IEnumerable<Employee> Search(string[] words)
{
var table = new DataTable{ Columns = {
{"str", typeof(string)},
} };
foreach (var word in words)
table.Rows.Add(SqlLikeEscape(word)); // make a function that escapes wildcards
using var connection = CreateConnection();
return connection.Query<Employee>(yourQueryHere, new
{
words = table.AsTableValuedParameter("dbo.StringList"),
});
}
I've datatable as follows
DataTable ex= new DataTable();
ex.Columns.Add("Object");
ex.Columns.Add("Found");
ex.Rows.Add("vase''s","True");
string keyword="vase''s";
DataRow [] drs = ex.Select("[Object] like '%" + keyword + "%'");
drs is always empty I've tried with equal I've the same results and I've tested in other frameworks I've the same also
what's wrong in my select statement ?!
Update
I realized that it's due to single quote is considered as one in the query statement but how can I do that search in a generic way
Modify your code to be like below
DataTable ex = new DataTable();
ex.Columns.Add("Object");
ex.Columns.Add("Found");
ex.Rows.Add("vase's", "True"); //don't escape here
string keyword = "vase''s";
//Use a equal to comparison rather than using LIKE operator
DataRow[] drs = ex.Select("[Object] = '" + keyword + "'");
The string value in your table literally has two single quotes. You do not escape single quotes in C# strings. The Select statement just needs two single quotes to tell the parser that the quote is not the end of the constant string value. So either keep a single quote in the value:
ex.Rows.Add("vase's", "True");
or search for a string with two single quotes:
string keyword = "vase''''s";
When you added that DataRow, you're informed the vase''s value (with two single quotes) and you (probably) don't want it.
However you MUST escape it in the query, like below:
DataTable ex = new DataTable();
ex.Columns.Add("Object");
ex.Columns.Add("Found");
ex.Rows.Add("vase's", "True");
foreach (DataRow row in ex.Rows)
Console.WriteLine(row["Object"]); // "vase's"
string keyword = "vase's";
DataRow[] drs = ex.Select("[Object] like '%" + keyword.Replace("'", "''") + "%'");
Console.WriteLine("{0} rows found", drs.Length);
Using C#, I'm getting an exception:
System.Data.EvaluateException: Cannot perform '=' operation on System.String and System.Int32.
I've traced it back to this code:
foreach (DataRow rows in dt.Rows){
...etc
string filter = string.Format("CUST_ID = " + rows[0]);
DataRow[] row = dt.Select(filter);
After the foreach I go to a bool which uses linq to find if a record with the same CUST_ID in DT exists in another datatable. If false then I go to the string filter. Here I take all of the records not existing in the second datatable and use SqlBulkCopy to put them all in a DB.
The funny thing is that just a hand full of records don't work. There are about 7000 of them and roughly 50 will cause this exception. I don't see any difference between the records. So, I'm not sure what I'm missing.
EDIT:
One of the records has CUST_ID = 998947 which would throw the exception.
I change the CUST_ID to 987654 and there was no exception.
You should enclose your input with apostrophes. Also, when using String.Format, provide format string and its parameters. Not just string, then is the String.Format call useless. Try this:
string filter = String.Format("CUST_ID = '{0}'", rows[0].ToString());
EDIT: Sorry, misread your question. Don't care about apostrophes if your column is of integer type. The exception tells you that you are trying to compare string with int. If you are sure, they are of int, just little code review from me:
string filter = String.Format("CUST_ID = {0}", rows[0].ToString());
You can also try to cast the CUST_ID to string and compare it with your value, but it isn't much nice solution.
I like to know is it possible to add parameter in datatable.select(expression).For example
string query="Name=#Name";
//dt is comming from database.
dt.Select(query);
How to add this parameter #Name. I need to compare a value which contains single quote and it gets failed in the above case.
Thanks in advance
You can use String.Format, you need to escape single quotes with two:
string query = string.Format("Name='{0}'", name.Replace(#"'", "''"));
var rows = dt.Select(query);
or, if you want to use Like:
string query = string.Format("Name LIKE '%{0}%'", name.Replace(#"'", "''"));
(note that a DataTable is not vulnerable to sql-injection since it's an in-memory object)
You can pass only expression to Select method.
In case if you need to pass the parameter dynamically then you can try this.
string Exp = "Name ='" + variable + "'";
dt.select(Exp);
Suppose that I want to create an SQL SELECT statement dynamically with reflection on primary key. I search in the table for primary keys and then, I make the statement.
Problem is, I don't know the type of fields that compose the primary key before getting them. So, if it's a string or date, I must add quotation marks but not if it's an int.
Atm, I am doing like that :
var type = field.GetType().Name;
if (type.ToLower().StartsWith("string") || type.ToLower().StartsWith("date"))
{
field = "\"" + field + "\"";
} else if (type.ToLower().StartsWith("char"))
{
field = "\'" + field + "\'";
}
With this code, I can handle some SQL types but there are a lot more.
My problem is that it's combined with LinQ. I got a DataContext object and a generic type table from the context. And context.ExecuteQuery only allows parameters to be passed has values. I also tried with Dynamic LinQ but I got the same problem
Does anyone know a better solution?
That is simply the wrong way to write SQL. Parameterize it and all these problems evaporate (as do problems with "which date format to use", etc. And of course the biggie: SQL injection.
Then it just becomes a case of adding #whatever into the TSQL, and using cmd.Parameters.AddWithValue("whatever", field) (or similar).
Update (from comments): since you mention you are using DataContext.ExecuteQuery, this becomes easier: that method is fully parameterized using the string.Format convention, i.e.
object field = ...;
var obj = db.ExecuteQuery<SomeType>(
"select * from SomeTable where Id = {0}", field).ToList(); // or Single etc
No string conversions necessary.
(the last parameter is a params object[], so you can either pass multiple discreet terms, or you can populate an object[] and pass that, if the number of terms is not fixed at compile-time; each term in the array maps by (zero-based) index to the {0}, {1}, {2}... etc token in the query)
Have you tried with parameters? For instance if you are using SQLServer as a database and you want to do this query:
"SELECT * FROM someTable WHERE id = " + field;
Then you should use sometething like this:
"SELECT * FROM someTable WHERE id = #field"
and add parameter to your command:
SqlParameter param1 = new SqlParameter("#field", field);
command.Parameters.Add(param1);
EDIT: Watch out that for different database providers the syntax for the SQL query is different, the same for the Access would be
"SELECT * FROM someTable WHERE id = ?";
command.Parameters.AddWithValue("field", field);