When using DbSet<T>.SqlQuery(), how to use named parameters? - sql

I'm a big fan of using named parameters instead of string-based parameter injection. It's type-safe and safe against most forms of SQL injection. In old ADO.NET, I would create a SqlCommand object and a bunch of SqlParameters for my query.
var sSQL = "select * from Users where Name = #Name";
var cmd = new SqlCommand(conn, sSQL);
cmd.Parameters.AddWithValue("#Name", "Bob");
cmd.ExecuteReader();
Now, in Entity Framework, it appears (on this link) to have regressed to a simple String.Format statement and string injection again: (simplified for discussion)
MyRepository.Users.SqlQuery("Select * from Users where Name = {0}", "Bob");
Is there a way to use named parameters with the Entity Framework DbSqlQuery class?

var param = new ObjectParameter(":p0", "Bob");
MyRepository.Users.SqlQuery("Select * from Users where Name = :p0", param);

Since I can't comment, I'm fixing the other answer:
var param = new ObjectParameter("p0", "Bob");
MyRepository.Users.SqlQuery("Select * from Users where Name = :p0", param);
You don't have to put a colon on the name when instantiating an ObjectParameter. That's why SLC got the error he mentioned in his comment.

Related

Access Update query not changing values [duplicate]

I have an SQL statement that I'm executing through OleDb, the statement is something like this:
INSERT INTO mytable (name, dept) VALUES (#name, #dept);
I'm adding parameters to the OleDbCommand like this:
OleDbCommand Command = new OleDbCommand();
Command.Connection = Connection;
OleDbParameter Parameter1 = new OleDbParameter();
Parameter1.OleDbType = OleDbType.VarChar;
Parameter1.ParamterName = "#name";
Parameter1.Value = "Bob";
OleDbParameter Parameter2 = new OleDbParameter();
Parameter2.OleDbType = OleDbType.VarChar;
Parameter2.ParamterName = "#dept";
Parameter2.Value = "ADept";
Command.Parameters.Add(Parameter1);
Command.Parameters.Add(Parameter2);
The problem I've got is, if I add the parameters to command the other way round, then the columns are populated with the wrong values (i.e. name is in the dept column and vice versa)
Command.Parameters.Add(Parameter2);
Command.Parameters.Add(Parameter1);
My question is, what is the point of the parameter names if parameters values are just inserted into the table in the order they are added command? The parameter names seems redundant?
The Problem is that OleDb (and Odbc too) does not support named parameters.
It only supports what's called positional parameters.
In other words: The name you give a parameter when adding it to the commands parameters list does not matter. It's only used internally by the OleDbCommand class so it can distinguish and reference the parameters.
What matters is the order in which you add the parameters to the list. It must be the same order as the parameters are referenced in the SQL statement via the question mark character (?).
But here is a solution that allows you to use named parameters in the SQL statement. It basically replaces all parameter references in the SQL statement with question marks and reorders the parameters list accordingly.
It works the same way for the OdbcCommand class, you just need to replace "OleDb" with "Odbc" in the code.
Use the code like this:
command.CommandText = "SELECT * FROM Contact WHERE FirstName = #FirstName";
command.Parameters.AddWithValue("#FirstName", "Mike");
command.ConvertNamedParametersToPositionalParameters();
And here is the code
public static class OleDbCommandExtensions
{
public static void ConvertNamedParametersToPositionalParameters(this OleDbCommand command)
{
//1. Find all occurrences of parameter references in the SQL statement (such as #MyParameter).
//2. Find the corresponding parameter in the commands parameters list.
//3. Add the found parameter to the newParameters list and replace the parameter reference in the SQL with a question mark (?).
//4. Replace the commands parameters list with the newParameters list.
var newParameters = new List<OleDbParameter>();
command.CommandText = Regex.Replace(command.CommandText, "(#\\w*)", match =>
{
var parameter = command.Parameters.OfType<OleDbParameter>().FirstOrDefault(a => a.ParameterName == match.Groups[1].Value);
if (parameter != null)
{
var parameterIndex = newParameters.Count;
var newParameter = command.CreateParameter();
newParameter.OleDbType = parameter.OleDbType;
newParameter.ParameterName = "#parameter" + parameterIndex.ToString();
newParameter.Value = parameter.Value;
newParameters.Add(newParameter);
}
return "?";
});
command.Parameters.Clear();
command.Parameters.AddRange(newParameters.ToArray());
}
}
Parameter NAMES are generic in the SQL support system (i.e. not OleDb specific). Pretty much ONLY OleDb / Odbc do NOT use them. They are there because OleDb is a specific implementation of the generic base classes.

Problem reading foreign characters Winform Textbox from database

please help. in Visual Studio 2017 and SQL localDB - WinForm learns and makes a small application. Form (Textbox), where "name, surname, address, city, phone and email" is written in Czech language containing "ěščřžýáíé" ". Everything is stored in the database (nvarchar) in order. Everything OK.
In Form2 I have another form where Combobox calls a "surname" and it has to fill in the phone and e-mail automatically from the database. If the surname is without the character "ěščřžýáíé", everything will be displayed correctly. If it contains "ěščřžýáíé", only the last name will be displayed, but the phone and email will not be loaded into the TextBox.
The code sample (without ěščřžýáíé) works perfectly:
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\newtest.mdf;Integrated Security=True");
string sql = "select * from test111 WHERE firmadat ='" + prijmeniComboBox.Text + "'; ";
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader myreader;
try
{
con.Open();
myreader = cmd.ExecuteReader();
while (myreader.Read())
{
string rollno = myreader.GetInt32(0).ToString();
string name = myreader.GetString(1);
string telephone = myreader.GetString(3);
string email = myreader.GetString(4);
textBox1.Text = rollno;
telefonTextBox.Text = telephone;
emailTextBox.Text = email;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Thank you for your help.
The problem is probably here:
string sql = "select * from test111 WHERE firmadat ='" + prijmeniComboBox.Text + "'; ";
Here you take your Unicode string and you concatenate it into a SQL string that is not a Unicode string. I would tell you how to get it working as you intended but this is a really really dangerous way to write SQLs. Someone at kids' toy maker VTech wrote SQLs this way and enabled a hacker to download millions of images of children taken by vtech devices. If one of my developers wrote SQL like this they would be subject to disciplinary action, possibly being fired.
I strongly recommend that you use parameterized SQLs; any number of internet resources will show you how, such as http://bobby-tables.com - it will also solve the problem of getting no results when searching using a search term that contains non-ASCII characters
Take a look at http://dapper-tutorial.net ; using dapper will not only take care of the parameterizing for you, but make your database life easier by reducing your code to just a couple of lines, something like:
using(SqlCommand x = new SqlCommand(conn)
{
var p = x.Query(
"select * from test111 WHERE firmadat = #a",
new { a = prijmeniComboBox.Text }
);
firstNameTextBox.Text = p.FirstName; //or what the column is called on db
...
}
You just write your sql, use #namedParameter placeholders and supply an anonymous object with properties named after the placeholders. Dapper does the rest. If you have a Person class in your project you can even get dapper to create it and populate it for you

SQL Server update in C#

I try to UPDATE data in my SQL Server database and I get this error:
System.Data.SqlClient.SqlException
Incorrect syntax near 'de'
Unclosed quotation mark after the character string ')'
private void BtEnrMod_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=.\\BD4X4;Initial Catalog=BD4X4;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = " + TxBxService.Text + ", Prix = " + TxBxPrix.Text + "WHERE Code = " + LbCodeAff.Text + "')", con);
int i = cmd.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("Service Modifié");
}
else
{
MessageBox.Show("Erreur");
}
this.Close();
con.Close();
}
Replace the one liner that declares your command with this code block:
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = #t, Prix = #p WHERE Code = #c", con);
cmd.Parameters.AddWithValue("#t", TxBxService.Text);
cmd.Parameters.AddWithValue("#p", TxBxPrix.Text);
cmd.Parameters.AddWithValue("#t", LbCodeAff.Text);
Always avoid writing an sql where you string concatenate in a value provided by the user in a text box; it's the number one security horror you can make with sql. Always use parameters to put values in, like you see here. For more info on this SQL injection hacking, see http://bobby-tables.com
If you ever fin yourself in a situation where you think you have to concatenate to make an sql, don't concatenate a value in; concatenate a parameter in and add the value into the parameters collection. Here's a hypothetical example:
var cmd = new SqlCommand("","connstr");
strSql = "SELECT * FROM table WHERE col IN (";
string[] vals = new[]{ "a", "b", "c" };
for(int x = 0; x<vals.Length; x++){
strSql += ("#p"+x+",");
cmd.Parameters.AddWithValue("#p"+x, vals[x]);
}
cmd.CommandText = strSql + ")";
This uses concatenation to make an sql of SELECT * FROM table WHERE col IN (#p0, #p1, #p2) and a nicely populated parameters collection
When you're done grokking that, read the link Larnu posted in the comments. There are good reasons to avoid using AddWithValue in various scenarios but it will always be preferable to concatenation of values. Never ditch the use of parameters "because I read a blog one time about how AddWithValue is bad" - form parameters using the new parameter constructor, or use AddWithValue shortcut, but never concat values
Or better still than all of this, use an ORM like Entity Framework, nHibernate or Dapper and leave most of this boring boilerplate low level SQL drudgery behind. These libraries do most of this wrangling for you; EF and nH even write th sql too, dapper you write it yourself but it takes care of everything else
Using a good ORM is like the difference between writing creating a UI manually line by line of position, font, anchor, event code for every button, label and text box versus using the windows forms designer; a world apart and there's no sense in taking hours to create manually what software can do more comprehensively, faster and safer for you in seconds

Oracle Parameters in .net sql queries - ORA-00933: SQL command not properly ended

I am trying to do create a where clause to pass as a parameter to an Oracle command and it's proving to be more difficult than I thought. What I want to do is create a big where query based off user input from our application. That where query is to be the single parameter for the statement and will have multiple AND, OR conditions in it. This code here works however isn't exactly what I require:
string conStr = "User Id=testschema;Password=pass12341;Data Source=orapdex01";
Console.WriteLine("About to connect to Database with Connection String: " + conStr);
OracleConnection con = new OracleConnection(conStr);
con.Open();
Console.WriteLine("Connected to the Database..." + Environment.NewLine + "Press enter to continue");
Console.ReadLine();
// Assume the connection is correct because it works already without the parameterization
String block = "SELECT * FROM TEMP_VIEW WHERE NAME = :1";
// set command to create anonymous PL/SQL block
OracleCommand cmd = new OracleCommand();
cmd.CommandText = block;
cmd.Connection = con;
// since execurting anonymous pl/sql blcok, setting the command type
// as text instead of stored procedure
cmd.CommandType = CommandType.Text;
// Setting Oracle Parameter
// Bind the parameter as OracleDBType.Varchar2
OracleParameter param = cmd.Parameters.Add("whereTxt", OracleDbType.Varchar2);
param.Direction = ParameterDirection.Input;
param.Value = "MY VALUE";
// Get returned values from select statement
OracleDataReader dr = cmd.ExecuteReader();
// Read the identifier for each result and display it
while (dr.Read())
{
Console.WriteLine(dr.GetValue(0));
}
Console.WriteLine("Selected successfully !");
Console.WriteLine("");
Console.WriteLine("***********************************************************");
Console.ReadKey();
If I change the lines below to be the type of result I want then I get an error "ORA-00933: SQL command not properly ended":
String block = "SELECT * FROM TEMP_VIEW :1";
...
...
param.Value = "WHERE NAME = 'MY VALUE' AND ID = 5929";
My question is how do I accomplish adding my big where query dynamically without causing this error?
Sadly there is no easy way to achieve this.
One thing you will need to understand with parameterised SQL in general is that bind parameters can only be used for values, such as strings, numbers or dates. You cannot put bits of SQL in them, such as column names or WHERE clauses.
Once the database has the SQL text, it will attempt to parse it and figure out whether it is valid, and it will do this without taking any look at the bind parameter values. It won't be able to execute the SQL without all of the values.
The SQL string SELECT * FROM TEMP_VIEW :1 can never be valid, as Oracle isn't expecting a value to immediately follow FROM TEMP_VIEW.
You will need to build up your SQL as a string and also build up the list of bind parameters at the same time. If you find that you need to add a condition on the column NAME, you add WHERE NAME = :1 to the SQL string and a parameter with name :1 and the value you wish to add. If you have a second condition to add, you append AND ID = :2 to the SQL string and a parameter with name :2.
Hopefully the following code should explain a little better:
// Initialise SQL string and parameter list.
String sql = "SELECT * FROM DUAL";
var oracleParams = new List<OracleParameter>();
// Build up SQL string and list of parameters.
// (There's only one in this somewhat simplistic example. If you have
// more than one parameter, it might be easier to start the query with
// "SELECT ... FROM some_table WHERE 1=1" and then append
// " AND some_column = :1" or similar. Don't forget to add spaces!)
sql += " WHERE DUMMY = :1";
oracleParams.Add(new OracleParameter(":1", OracleDbType.Varchar2, "X", ParameterDirection.Input));
using (var connection = new OracleConnection() { ConnectionString = "..."})
{
connection.Open();
// Create the command, setting the SQL text and the parameters.
var command = new OracleCommand(sql, connection);
command.Parameters.AddRange(oracleParams.ToArray());
using (OracleDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
// Do stuff with the data read...
}
}
}

How to use SQL wildcards in LINQ to Entity Framework

I have a query that looks like this:
IQueryable<Profile> profiles = from p in connection.Profiles
where profile.Email.Contains(txtSearch)
select p;
I know that when this is converted to SQL it uses a LIKE '%<value of txtSearch>%' but if txtSearch = "jon%gmail.com" it converts it to `LIKE '%jon~%gmail.com%'. The ~ escapes the % in the middle that is a wild card. How do I get around that? I need to be able to put wild cards into my LINQ to EF searches.
I'm not sure that this is possible directly with linq because you can call only basic string functions like Contains, StartsWith or EndsWith. It is possible with Entity SQL so you can combine these approaches.
var query = new ObjectQuery<Profile>(
#"SELECT VALUE p
FROM CsdlContainerName.Profiles AS p
WHERE p.Email LIKE '" + wildcardSearch + "'",
context);
var result = query.AsQueryable().OrderByDescending(p => p.Name).ToList();
ESQL injection strikes back :)
Second version without injection vulnerability (I didn't try it but it should work):
var commandText =
#"SELECT VALUE p
FROM CsdlContainerName.Profiles AS p
WHERE p.Email LIKE #search";
var query = new ObjectQuery<Profile>(commandText, context);
query.Parameters.Add(new ObjectParameter("search", wildcardSearch));
var result = query.AsQueryable().OrderByDescending(p => p.Name).ToList();