So I recently learned that I should absolutely be using parametrized query's to avoid security issues such as SQL injection. That's all fine and all, I got it working.
This code shows some of the code how I do it:
param1 = new SqlParameter();
param1.ParameterName = "#username";
param1.Value = username.Text;
cmd = new SqlCommand(str, sqlConn);
cmd.Parameters.Add(param1);
//and so on
But the problem is, I have over 14 variables that needs to be saved to the db, it's like a registration form. And it would look really messy if I have to write those lines 14 times to parametrize each variable. Is there a more dynamic way of doing this? Like using a for loop or something and parametrizing every variable in the loop somehow?
Use single line SqlParameterCollection.AddWithValue Method
cmd.Parameters.AddWithValue("#username",username.Text);
or other variation you might try like this
command.Parameters.Add(new SqlParameter("Name", dogName));
Here you go... via dapper:
connextion.Execute(sql, new {
username = username.Text,
id = 123, // theses are all invented, obviously
foo = "abc",
when = DateTime.UtcNow
});
that maps to ExecuteNonQuery, but there are other methods, such as Query<T> (binds the data very efficiently by name into objects of type T per row), Query (like Query<T>, but uses dynamic), and a few others (binding multiple grids or multiple objects, etc). All ridiculously optimized (IL-level meta-programming) to be as fast as possible.
Another technique, you can use..
List<SqlParameter> lstPrm = new List<SqlParameter>();
lstPrm.Add(new SqlParameter("#pusername", usernameValue ));
lstPrm.Add(new SqlParameter("#pID", someidValue));
lstPrm.Add(new SqlParameter("#pPassword", passwordValue));
Add the end you can iterate to insert the parameters in your command object
Use my SqlBuilder class. It lets you write paramaterized queries without ever creating a parameter, or having to worry about what its called. Your code will look like this...
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. Compared to concatenated queries, you don't even need extra lines. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
Better still, use my shiny new Visual Studio extension. You declare your parameters in your sql, intact in its own file. My extension will run your query when you save your file, and will make you a wrapper class to call at runtime, and a results class to access your results, with intellisense all over da place. You will see your sql parameters as arguments to the Execute() methods of the wrapper class. You will never have to write another line of parameter code in C#, or reader code, or cmd, or even connection (unless you want to manage that yourself). Gone gone gone :-)
Related
I find it unnecessarily cumbersome to create prepared statements in C# generally what you do is something like this:
public T GetData<T>(string userInput)
{
string selectSomething = "SELECT * FROM someTable where someCol = #userInput";
using (IDbCommand command = new SqlCommand(selectSomething))
{
IDbDataParameter parameter = new SqlParameter("#userInput", SqlDbType.NVarChar);
parameter.Value = userInput;
command.Parameters.Add(parameter);
IDataReader reader = command.ExecuteReader();
reader.Read();
}
...
}
But now as there are interpolated strings it could be as easy like this:
public T GetData<T>(string userInput)
{
string selectSomething = $"SELECT * FROM someTable where someCol = {userInput}";
using (IDbCommand command = new SqlCommand(selectSomething))
{
IDataReader reader = command.ExecuteReader();
reader.Read();
}
}
There's still some other boilerplate code but still an improvement. Is there maybe a way to get the comfort of interpolated strings but still keep the safety of prepared statements with something like this:
string selectSomething = $"SELECT * FROM someTable where someCol = {userInput.PreventSQLInjections()}";
If you don't want to use EF which has FromSqlInterpolated method or any other ORM which will help you to handle your data access you can leverage fact that compiler uses FormattableString type to handle string interpolation to write helper method looking something like this (not fully working, but you should get the idea) to remove the boilerplate code:
public static class SqlCommandEx
{
// maps CLR type to SqlDbType
private static Dictionary<Type, SqlDbType> typeMap;
static SqlCommandEx()
{
typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
//... all other type maps
}
public static SqlCommand FromInterpolatedString(FormattableString sql)
{
var cmdText = sql.Format;
int count = 0;
var #params = new IDbDataParameter[sql.ArgumentCount];
foreach (var argument in sql.GetArguments())
{
var paramName = $"#param_{count}";
cmdText = cmdText.Replace($"{{{count}}}", paramName);
IDbDataParameter parameter = new SqlParameter(paramName, typeMap[argument.GetType()]);
parameter.Value = argument;
#params[count] = parameter;
count++;
}
var sqlCommand = new SqlCommand(cmdText);
sqlCommand.Parameters.AddRange(#params);
return sqlCommand;
}
}
And usage:
using (IDbCommand command = SqlCommandEx.FromInterpolatedString($"Select * from table where id = {val}"))
{
...
}
But this comes close to writing your own ORM which you usually should not do.
Prepared statements/parameterized queries go further than just sanitizing or escaping the inputs. When you use parameterized queries, the parameter data are sent as separate values from the SQL statement. The parameter data is never substituted directly into the SQL, and therefore injection is perfectly protected in a way that escaping/sanitizing the input never will.
In other words, DON'T COUNT ON STRING INTERPOLATION FOR "FIXING" SQL PARAMETERS!
Moreover, it's really not that much extra work. What the question shows is the hard way for adding parameters. You can simplify that code like this:
public T GetData<T>(string userInput)
{
string selectSomething = "SELECT * FROM someTable where someCol = #userInput";
using (IDbCommand command = new SqlCommand(selectSomething))
{
command.Parameters.Add("#userInput", SqlDbType.NVarChar).Value = userInput;
IDataReader reader = command.ExecuteReader();
reader.Read();
}
...
}
This gets the extra work for parameters down to one line of code per parameter.
If you have high-confidence in the mapping between C# types and SQL types, you can simplify even further like this:
command.Parameters.AddWithValue("#userInput", userInput);
Just be careful with that shortcut: if ADO.Net guesses the SQL data type wrong, it can break indexing and force per-row type conversions, which can really kill performance.
The following query in C# doesn't work, but I can't see the problem:
string Getquery = "select * from user_tbl where emp_id=#emp_id and birthdate=#birthdate";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
Reader.HasRows returns no result but when I query it to my database I got data.
I'll assume your code is actually not quite as presented, given that it wouldn't currently compile - you're using cmdR before you declare it.
First, you're trying to use named parameters, and according to the documentation of OdbcCommand.Parameters, that isn't supported:
When CommandType is set to Text, the .NET Framework Data Provider for ODBC does not support passing named parameters to an SQL statement or to a stored procedure called by an OdbcCommand. In either of these cases, use the question mark (?) placeholder.
Additionally, I would personally avoid using AddWithValue anyway - I would use something like:
string sql = "select * from user_tbl where emp_id = ? and birthdate = ?";
using (var connection = new OdbcConnection(...))
{
connection.Open();
using (var command = new OdbcCommand(sql, connection))
{
command.Parameters.Add("#emp_id", OdbcType.Int).Value = userValidate.EmployeeId;
command.Parameters.Add("#birthdate", OdbcType.Date).Value = userValidate.BirthDate;
using (var reader = command.ExecuteReader())
{
// Use the reader here
}
}
}
This example uses names following .NET naming conventions, and demonstrates properly disposing of resources... as well as fixing the parameter issue.
I do think it's slightly unfortunate that you have to provide a name for the parameter when adding it to the command even though you can't use it in the query, but such is life.
Use like this:
string Getquery = "select * from user_tbl where emp_id=? and birthdate=?";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
while(Reader.Read())
{
//Do something;
}
I know this thread is old, but I wanted to share my solution for anyone else coming up on this.
I was having issues with the typical method that Jon posted. I have used it before, but for some reason with this new string I had it was not wanting to actually place the parameter correctly and was causing the reader to not work.
I ended up doing something like this instead, since in the end we are just replacing parts of a string.
string sql = "select * from user_tbl where emp_id = "+ var1 +" and birthdate = "+
var2""
OdbcCommand command = new OdbcCommand(sql);
This was easier for me to get to work. Be warned though, I am not sure if it has any specific drawbacks when compare to using the command parameter method.
I am exploring Silverlight (C#) and SQLServer as a next evolution for our current (slow) Access database. So far everything has been great, using DomainServices to retrieve the data I need. In our database we have a table (Supervisors) with Supervisor_ID, Supervisor_FirstName, Supervisor_LastName and many other fields.
What I want to do is recreate a function I use in my current database called EntityNameFirstLast(EntityID) which would take an integer. I could then retrieve the value of [Supervisor_FirstName] from [Supervisors] table where [Supervisor_ID] == EntityID using the following:
FirstName = DLookup("[Supervisor_FirstName]", "Supervisors", "[Supervisor_ID] = EntityID
I would do the same for lastname and combine the strings returning one string with First and last name.
How can I get just a single value from my database through my DomainService (or any way for that matter)? I understand that IQueryable GetSupervisorByID(Int SupID) will return the entire row that I need, but how can I get a specific field from that row?
I am also aware that I can set the DomainDataSource in my XAML and then bind to the data I want, but I am curious if what I asked above is doable or not.
There are number of ways you can accomplish your requirement if what you need is a single value from MS-SQL server:
1.Use a Query to do the concatenation and then use its output in your code
Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = EntityID
Now you can get the above query to execute through a SqlCommand and get the part thats interesting to you
private string GetSupervisorFullName(string entityID, string connectionString) {
string query = "Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = #EntityID";
string supervisorFullname = "";
using(SqlConnection con = new SqlConnection(connectionString)) {
SqlCommand cmdSupervisorFullname = new SqlCommand();
cmdSupervisorFullname.Connection = con;
cmdSupervisorFullname.CommandText = query;
cmdSupervisorFullname.CommandType = CommandType.Text;
SqlParameter paraEntityID = new SqlParameter();
paraEntityID.ParameterName = "#EntityID";
paraEntityID.SqlDbType = SqlDbType.NVarChar;
paraEntityID.Direction = ParameterDirection.Input;
paraEntityID.Value = entityID;
cmdSupervisorFullname.Parameters.Add(paraEntityID);
try {
con.Open();
supervisorFullname = (String) cmdSupervisorFullname.ExecuteScalar();
} catch(Exception ex) {
Console.WriteLine(ex.Message);
}
return supervisorFullname;
}
}
2.Second way would be create a Scalar function in the SQL for your requirement and then access that function using the same kind of method as mentioned above.
Then finally you would take the return value from your method GetSupervisorFullName and populate any control value of your choice.
Please do note that there are again other methods of doing the same with LINQtoSQL or with any other ORM tools. The above 2 methods are the basic way of accomplishing them.
Hope that helps.
I'm trying to find optimal (fast vs easiest) way to access SQL Server code thru code in C#.
As I was learning from books I've encountered multiple suggestions usually telling me to do it via drag and drop. However since I wanted to do it in code first approach was to get data by column numbers, but any reordering in SQL Query (like adding/removing columns) was pain for me to fix.
For example (don't laugh, some code is like 2 years old), I even coded special function to pass sqlQueryResult and check if it's null or not):
public static void exampleByColumnNumber(string varValue) {
string preparedCommand = #"SELECT TOP 1 [SomeColumn],[SomeColumn2]
FROM [Database].[dbo].[Table]
WHERE [SomeOtherColumn] = #varValue";
SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
sqlQuery.Prepare();
sqlQuery.Parameters.AddWithValue("#varValue) ", varValue);
SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
if (sqlQueryResult != null) {
while (sqlQueryResult.Read()) {
string var1 = Locale.checkForNullReturnString(sqlQueryResult, 0);
string var2 = Locale.checkForNullReturnString(sqlQueryResult, 1);
}
sqlQueryResult.Close();
}
}
Later on I found out it's possible thru column names (which seems easier to read with multiple columns and a lot of changing order etc):
public static void exampleByColumnNames(string varValue) {
string preparedCommand = #"SELECT TOP 1 [SomeColumn],[SomeColumn2]
FROM [Database].[dbo].[Table]
WHERE [SomeOtherColumn] = #varValue";
SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
sqlQuery.Prepare();
sqlQuery.Parameters.AddWithValue("#varValue) ", varValue);
SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
if (sqlQueryResult != null) {
while (sqlQueryResult.Read()) {
string var1 = (string) sqlQueryResult["SomeColumn"];
string var2 = (string) sqlQueryResult["SomeColumn2"];
}
sqlQueryResult.Close();
}
}
And 3rd example is by doing it by column names but using .ToString() to make sure it's not null value, or by doing If/else on the null check.
public static void exampleByColumnNamesAgain(string varValue) {
string preparedCommand = #"SELECT TOP 1 [SomeColumn],[SomeColumn2], [SomeColumn3]
FROM [Database].[dbo].[Table]
WHERE [SomeOtherColumn] = #varValue";
SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
sqlQuery.Prepare();
sqlQuery.Parameters.AddWithValue("#varValue) ", varValue);
SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
if (sqlQueryResult != null) {
while (sqlQueryResult.Read()) {
string var1 = (string) sqlQueryResult["SomeColumn"].ToString();
DateTime var2;
DateTime.TryParse(sqlQueryResult["SomeColumn2"].ToString());
int varInt = ((int) sqlQueryResult["SomeColumn3"] == null ? 0 : (int) sqlQueryResult["SomeColumn3"];
}
sqlQueryResult.Close();
}
}
Please bare in mind that I've just created this for sake of this example and there might be some typos or some slight syntax error, but the main question is which approach is best, which is the worst (I know first one is the one that I dislike the most).
I will soon have to start / rewriting some portion of my little 90k lines app which has at least those 3 examples used widely, so i would like to get best method for speed and preferably easiest to maintain (hopefully it will be same approach).
Probably there are some better options out there so please share?
It seems you may be looking at old books. If you're going to do it the "old fashioned way", then you should at least use using blocks. Summary:
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(commandString, connection))
{
using (var reader = command.ExecuteReader())
{
// Use the reader
}
}
}
Better still, look into Entity Framework.
Links: Data Developer Center
If it's easy you're looking for, you can't do any better than Linq-to-SQL:-
http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx
If your SQL database already exists, you can be up-and-running in seconds.
Otherwise, I agree with John.
you should have a look into these tutorials,
[http://www.asp.net/learn/data-access/][1]
All the work you are planning is already been done.
have a look at this way of doing same what you are doinng
string preparedCommand =
#"SELECT TOP 1 [SomeColumn],[SomeColumn2], [SomeColumn3]
FROM [Database].[dbo].[Table]
WHERE [SomeOtherColumn] = #varValue";
[1]: http://www.asp.net/learn/data-access/
More better way of doing the same above is by Using LINQ TO SQL
var result = from someObject in SomeTable
where SomeColumnHasValue == ValueToCompare
select new { SomeColumn, SomeColumn1, SomeColumn2};
No Type Safety Issues
Visualise Database in C# while you
work on it
at compile time less errors
less code
more productive
Following are some of the great resources for LINQ if you are interested
http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
http://www.hookedonlinq.com/MainPage.ashx
https://stackoverflow.com/questions/47740/what-are-some-good-linq-resouces
Hope it helps
If you're looking into using just straight ADO.net you might want to go out and find Microsoft's Enterprise Library's Data Access Application Block . David Hayden has a decent article that goes into some detail about using it.
Good luck and hope this helps some.
The easiest way to do data access in C#, to my mind, is using typed DataSets. A lot of it really is drag-and-drop, and it's even easier in .NET 2.0+ than in .NET 1.0/1.1.
Have a look at this article, which talks about using typed DataSets and TableAdapters:
Building a DAL using Strongly Typed TableAdapters and DataTables in VS 2005 and ASP.NET 2.0
A typed DataSet is basically a container for your data. You use a TableAdapter to fill it (which happens with SQL or stored procs, whichever you prefer) and to update the data afterwards. The column names in each DataTables in your DataSet are autogenerated from the SQL used to fill them; and relations between database tables are mirrored by relations between DataTables in the DataSet.
Don't convert data to strings only to try to parse it; DataReaders have methods to convert SQL data to .Net data types:
using (var connection = new SqlConnection(Locale.sqlDataConnection))
using (var command = new SqlCommand(preparedCommand, connection))
using (var reader = command.ExecuteReader())
{
int stringColumnOrdinal = reader.GetOrdinal("SomeColumn");
int dateColumnOrdinal = reader.GetOrdinal("SomeColumn2");
int nullableIntColumnOrdinal = reader.GetOrdinal("SomeColumn3");
while (reader.Read())
{
string var1 = reader.GetString(stringColumnOrdinal);
DateTime var2 = reader.GetDateTime(dateColumnOrdinal);
int? var3 = reader.IsDBNull(nullableIntColumnOrdinal) ? null : (int?)reader.GetInt32(nullableIntColumnOrdinal);
}
}
I test the many different ways for get data from sql server database and i faced & found fastest way is following:
First of all create class with "IDataRecord" parameterized method as per your required properties.
class emp
{
public int empid { get; set; }
public string name { get; set; }
public static emp create(IDataRecord record)
{
return new emp
{
empid = Convert.ToInt32(record["Pk_HotelId"]),
name = record["HotelName"].ToString()
};
}
}
Now create method for get data as below:
public List<S> GetData<S>(string query, Func<IDataRecord, S> selector)
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
cmd.Connection.Open();
using (var r = cmd.ExecuteReader())
{
var items = new List<S>();
while (r.Read())
items.Add(selector(r));
return items;
}
}
}
And then call function like:
var data = GetData<emp>("select * from employeeMaster", emp.create);
In my code I have this everywhere
command.Parameters.Add("#name", DbType.String).Value = name;
Is there an easier way? I would love to do something like
command.command.CommandTextFn("insert into tbl(key,val) values(?, ?);", key, value);
and have it figure out if the key/value is a string or int. I wouldn't mind if I had to use {0} instead of ?
Use the AddWithValue method:
command.Parameters.AddWithValue( "#name", name );
You could use this in conjunction with an extension method:
public static SqlCommand CreateCommand(this SqlConnection connection, string command, string[] names, object[] values )
{
if (names.Length != values.Length)
{
throw new ArgumentException("name/value mismatch");
}
var cmd = connection.CreateCommand();
cmd.CommandText = command;
for (int i = 0; i < names.Length; ++i )
{
cmd.Parameters.AddWithValue(names[i], values[i]);
}
return cmd;
}
used as
var command = connection.CreateCommand( "insert into tbl (key,val) values(#key,#val)",
new string[] { "#key", "#val" },
new object[] { key, val } );
Using parametrized queries protects your system against SQL injection attacks.
Of course, you can deal with SQL escaping, but why bother? You can make a mistake and just discover it when its too late.
Please read this article as it explain pros/cons using parametrized queries.
Invest your time improving other code pieces.
You're doing the right thing - using parameters is the best solution both for reuse of code and to protect you from SQL injection attacks.. It ain't broke so don't fix it!
If it really bugs you (and it shouldn't) then you could probably do something creative with an extension method to allow you wrap that bit of code up in something a bit smaller but in truth there are better things to worry about.
Create a set of generic functions that take the parameter name and the value. Wrap and hide away all your boilerplate code.
Try to replace such calls with linq to sql. You will gain type checking on compile time and it will be no longer needed to escape strings and think about sql injections.
sample:
Dim newCustomer = New Customer With {.CustomerID = "MCSFT", .CompanyName = "Microsoft", .ContactName = "John Doe", .ContactTitle = "Sales Manager", .Address = "1 Microsoft Way", .City = "Redmond", .Region = "WA", .PostalCode = "98052", .Country = "USA", .Phone = "(425) 555-1234", .Fax = Nothing}
db.Customers.Add(newCustomer)
db.SubmitChanges()