Reputation: 2487
I've heard of parametized sql queries a long time ago but I never really gave any attention to it as I'm used to writing full sql statements. I know it improves security against sql injection so now might be a very good time to adapt change even if it's too late. I got this one from this site http://www.codinghorror.com/blog/2005/04/give-me-parameterized-sql-or-give-me-death.html. So all credits go to the author. I've noticed all examples of parametized sql queries have paremeter count limitations. An example is the one shown below.
SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
string s = "SELECT email, passwd, login_id, full_name FROM members WHERE email = @email";
SqlCommand cmd = new SqlCommand(s);
cmd.Parameters.Add("@email", email);
SqlDataReader reader = cmd.ExecuteReader();
This is just equivalent to
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x';
I've learned programming all on my own through reading online tutorials and I really got nobody to help me on this. The reason I got stuck with full sql statement queries is the fact that I can't figure out how to do parametized sql queries with unlimited parameters. Is there any way to do this? The example only accepts 1 parameter which is the 'email' field. And it selects 4 fields from the given sql statement. My question is... is there any way we can do parametized sql queries with 5, 6, 7, or 100 selected fields, as well as the conditions under the WHERE clause? If this is possible, it will be particularly helpful when using INSERT statement. Thank you very much.
This example is in C#, but any VB.NET or same C# implementations are greatly appreciated. Thanks.
Upvotes: 2
Views: 5523
Reputation: 1
i think you just have to change this
cmd.Parameters.Add("@email", email);
to
cmd.Parameters.AddWithValue("@email", email);
Upvotes: 0
Reputation: 7830
One possible solution would be to pass the parameters name and value using a Dictionary object. A Dictionary object holds a collection of keys and values, and this is exactly what a single SqlParameter is - a single Key/Value container in the form:
Key = @Value
A collection can hold an arbitrary count of items, for example:
new Dictionary<String, Object>
{
{ "@Name", "Anonymous" },
{ "@Age", 25 },
{ "@Street", "Highway" },
{ "@Number", "1001" },
{ "@City", "NoName" }
}
In the example above, the key is of type String and the value of type Object. Object allows parameter values of arbitrary types (the explanation comes later in the code examples).
One possibility to create dynamic SQL statements would be:
The code can look like this:
// extract all repetitive tasks
// Create an array of SqlParameters from the given Dictionary object.
// The parameter value is of type Object in order to allow parameter values of arbitrary types!
// The .NET Framework converts the value automatically to the correct DB type.
// MSDN info: http://msdn.microsoft.com/en-us/library/0881fz2y%28v=vs.110%29.aspx
private SqlParameter[] dictionaryToSqlParameterArray(Dictionary<string, object> parameters)
{
var sqlParameterCollection = new List<SqlParameter>();
foreach (var parameter in parameters)
{
sqlParameterCollection.Add(new SqlParameter(parameter.Key, parameter.Value));
}
return sqlParameterCollection.ToArray();
}
// sqlQuery is the complete parametrised query
// for example like: INSERT INTO People(Name, Age, Street) VALUES(@Name, @Age, @Street)
private SqlCommand createSqlCommand(String sqlQuery, Dictionary<String, object> parameters)
{
SqlCommand command = new SqlCommand(sqlQuery);
command.Parameters.AddRange(dictionaryToSqlParameterArray(parameters));
return command;
}
Now a call with dynamic count of parameters will look like this:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string sqlQuery = "SELECT email, passwd, login_id, full_name FROM members WHERE email = @email AND name = @name";
// using the newly created method instead of adding/writing every single parameter manually
SqlCommand command = createSqlCommand(sqlQuery, new Dictionary<String, Object>
{
{ "@email", "[email protected]" },
{ "@name", "test" }
});
SqlDataReader reader = command.ExecuteReader();
It is a good start, but this is still not very dynamic, since we need to write the complete query every time (we save only the typing of command.Parameters.Add). Let's change this in order to type even less:
// create a parametrised SQL insert command with arbitrary count of parameters for the given table
private SqlCommand createSqlInsert(String tableName, Dictionary<String, object> parameters)
{
// the sql insert command pattern
var insertQuery = @"INSERT INTO {0}({1}) VALUES({2})";
// comma separated column names like: Column1, Column2, Column3, etc.
var columnNames = parameters.Select (p => p.Key.Substring(1)).Aggregate ((h, t) => String.Format("{0}, {1}", h, t));
// comma separated parameter names like: @Parameter1, @Parameter2, etc.
var parameterNames = parameters.Select (p => p.Key).Aggregate ((h, t) => String.Format("{0}, {1}", h, t));
// build the complete query
var sqlQuery = String.Format(insertQuery, tableName, columnNames, parameterNames);
// debug
Console.WriteLine(sqlQuery);
// return the new dynamic query
return createSqlCommand(sqlQuery, parameters);
}
// create a parametrised SQL select/where command with arbitrary count of parameters for the given table
private SqlCommand createSqlWhere(String tableName, Dictionary<String, object> parameters)
{
// the sql select command pattern
var whereQuery = @"SELECT * FROM {0} WHERE {1}";
// sql where condition like: Column1 = @Parameter1 AND Column2 = @Parameter2 etc.
var whereCondition = parameters.Select (p => String.Format("{0} = {1}", p.Key.Substring(1), p.Key)).Aggregate ((h, t) => String.Format("{0} AND {1}", h, t));
// build the complete condition
var sqlQuery = String.Format(whereQuery, tableName, whereCondition);
// debug
Console.WriteLine(sqlQuery);
// return the new dynamic query
return createSqlCommand(sqlQuery, parameters);
}
Now the creation of a SELECT command will look like this:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// specify only table name and arbitrary count of parameters
var getPersonSqlCommand = createSqlWhere("People", new Dictionary<String, Object>
{
{ "@Name", "J.D." },
{ "@Age", 30 }
});
SqlDataReader reader = getPersonSqlCommand.ExecuteReader();
The createSqlWhere method will return an initialized SqlCommand with the query:
SELECT * FROM People WHERE Name = @Name AND Age = @Age
The INSERT will also be short:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// specify only table name and arbitrary count of parameters
var addPersonSqlCommand = createSqlInsert("People", new Dictionary<String, Object>
{
{ "@Name", "Anonymous" },
{ "@Age", 25 },
{ "@Street", "Test" }
});
addPersonSqlCommand.ExecuteNonQuery();
The corresponding SQL command will look like this:
INSERT INTO People(Name, Age, Street) VALUES(@Name, @Age, @Street)
Other SQL commands like DELETE, UPDATE and so on can be created in the same way. New parameters should be added only in one place - in the dictionary.
Admittedly the initial effort is more than just writing one method, but it will pay off if the new methods are used more than once (a couple of times) - for example five parametrised selects and/or inserts on five different tables with different parameters, which is certainly always the case in small and average sized software projects.
Upvotes: 4
Reputation: 646
Just create a method like :
public void unlimQuery(string query,params object[] args)
{
SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
string s =query;
SqlCommand cmd = new SqlCommand(s);
For(int i=0;i< args.Length;i++)
cmd.Parameters.Add("@param"+i, args[i]);
SqlDataReader reader = cmd.ExecuteReader();
}
Example :
unlimQuery("INSERT INTO CUSTOMERS(ID,NAME,AGE,ADRESS,COUNTRY) VALUES(@param0,@param1,@param2,@param3,@param4)",5,"Ali",27,"my City","England");
Explanation :
the params keyword in c# gives you the possibility to insert unlimited arguments of the indicated type, so the arguments added (5,"Ali",27,"my City","England") will be casted to an array of objects then passed to the Method
inside the method you'll get an array of objects, so for each object you create a parameter, his alias is @paramX
where X is the index of the argument (in the params array), then sqlCommad will replace each alias by its value defined in the cmd.Parameters.Add("@param"+i, args[i])
clause
so @param0 => 5, .....
Upvotes: 1