Implementing a query function with undeclared number of parameters? - c#

I'm developing a server/client application in C#. In the earlier phases of development I was writing SQL codes each time and it caused spaghetti code. Now I'm trying to make it clean. My question is: How can I write a general query generator function with dynamic parameters?
private void button3_Click(object sender, EventArgs e)
{
try
{
SqlCommand cmd= new SqlCommand();
cmd.CommandText = "INSERT INTO tbl(line, name, firstvalue, secondvalue)" +
"VALUES(#line, #name, #value, #secondvalue)";
cmd.Parameters.AddWithValue("#line", comboBox4.Text);
cmd.Parameters.AddWithValue("#name", textBox2.Text);
cmd.Parameters.AddWithValue("#value", comboBox5.Text);
cmd.Parameters.AddWithValue("#secondvalue", comboBox6.Text);
cmd.Connection = Db.Db;
cmd.CommandType = CommandType.Text;
SqlDataReader dr = cmd.ExecuteReader();
MessageBox.Show("Saved");
}
catch (Exception ex)
{
MessageBox.Show(ex);
}
finally
{
Db.Close();
}
}
But I want to convert it into:
public void query(string commandText, params string[] parameters)
{
SqlCommand command = new SqlCommand();
command.CommandText = commandText;
foreach (var parameter in parameters)
//In this part, there can be lots of different queries and how can assign each parameter to relevant values
//Is there any change to assign them in order.
}

Well, if you insist on implementing such a routine (usually we use ORM) you have to parse the commandText; the simplest (but not the best) implementation is regular expressions (we Match parameter name within commandText, then Zip it with its value from parameters):
using System.Linq;
using System.Text.RegularExpressions;
...
public void query(string commandText, params string[] parameters) {
using (SqlCommand command = new SqlCommand()) {
command.Connection = myConnection; //TODO: put the right connection here
command.CommandText = commandText;
var prms = Regex
.Matches(commandText, #"\b#[A-Za-z_][A-Za-z_0-9]*\b")
.Cast<Match>()
.Zip(parameters, (match, value) => new {
name = match.Value,
value
});
foreach(var prm in prms)
command.Parameters.AddWithValue(prm.name, prm.value);
// Just execute; we have nothing to read (ExecuteReader)
command.ExecuteNonQuery();
}
}
Edit: If you want / ready to specify parameters' names, not only values you can try Tuples: for c# 7.0+
public void query(string commandText, params (string, object)[] parameters) {
...
foreach (var prm in parameters)
command.Parameters.AddWithValue(prm.Item1, prm.Item2);
...
}
usage
query(sql,
("#line", comboBox4.Text),
("#name", textBox2.Text),
("#id", 123), // please, note integer value
("#money", 456.78d),
...
);
for C# 6.0-:
public void query(string commandText, params Tuple<string, object>[] parameters) {
...
foreach (var prm in parameters)
command.Parameters.AddWithValue(prm.Item1, prm.Item2);
...
}
...
query(sql,
Tuple.Create("#line", comboBox4.Text),
Tuple.Create("#name", textBox2.Text),
...
);

Create an object with database parameters and set all values in it. Use Entity framework to do the rest or read from object when assigning.

I solved it and here is the solution. We will store them in the same array. So the parameters array will be:
parameters[0] = "#line"
parameters[1] = line
parameters[2] = "#name"
parameters[3] = name
and while posting them into AddWithValue() function
for (int i = 0; i < parameters.Length;)
{
command.Parameters.AddWithValue(parameters[i++] as string,parameters[i++]);
}
and we should call the query function when needed like this,
string commandText = "INSERT INTO tbl(line, name, firstvalue, secondvalue)" +
"VALUES(#line, #name, #value, #secondvalue)";
query("#line", line, "#name", name, "#value", firstvalue, "secondvalue", secondvalue);

Function Body
public class Database
{
public string executeScaler(string commandText, bool isStoredProcedure, Dictionary<string, object> dictParams = null)
{
string result = "";
DataTable dt = new DataTable();
SqlConnection con = ConnectionStrings.GetConnection();
SqlCommand cmd = new SqlCommand(commandText, con);
if (isStoredProcedure)
cmd.CommandType = CommandType.StoredProcedure;
if (dictParams != null)
foreach (KeyValuePair<string, object> rec in dictParams)
cmd.Parameters.AddWithValue("#" + rec.Key, rec.Value);
try
{
result = Convert.ToString(cmd.ExecuteScalar());
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
con.Dispose();
}
return result;
}
}
Calling Function
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("Email", obj.Email);
dict.Add("Password", obj.Password);
Database objDB = new Database();
objDB.executeScaler("RegisterAccount", true, dict);

Related

Use two lists of values as parameters in a method and generate objects as many as the length of the two lists C#

I have the following two functions:
//-----------------------------------FUNCTION 4-----------------------------------
/// <summary>
/// Call the specified procedure that will import file1 in SQL Server
/// </summary>
/// <param name="connectionString"> The connection string to SQL server instance</param>
/// <param name="importedfilepath"> The path of the browsed file</param>
public static void LoadLayout(string connectionString, string importedfilepath)
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "createselectedtableinDB";
command.Parameters.AddWithValue("#TableName", importedfilepath);
command.ExecuteNonQuery();
if (command.ExecuteNonQuery() > 0)
MessageBox.Show("Selected file was loaded successfully");
}
}
}
//-----------------------------------FUNCTION 5-----------------------------------
/// <summary>
/// Call the specified procedure that will import file2 in SQL Server
/// </summary>
/// <param name="connectionString"> The connection string to SQL server instance</param>
/// <param name="importedfilepath"> The path of the browsed file</param>
public static void LoadBusinessChecks(string connectionString, string importedfilepath)
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "createselectedtableinDB";
command.Parameters.AddWithValue("#TableName", importedfilepath);
command.ExecuteNonQuery();
if (command.ExecuteNonQuery() > 0)
MessageBox.Show("Selected file was loaded successfully");
}
}
}
As you can see the two functions take two parameters (which vary):
Parameter 1: connection string to SQL server
Parameter 2: FilePath
With those 2 parameters I am ok so far. What I want is to further optimize those two functions and basically have 1 function instead of those two. To do so, I know that I need to somehow specify:
Proceduce name:
command.CommandText = "createselectedtableinDB"; //can take different values
Procedure parameters and values (2 equal length list)
command.Parameters.AddWithValue("#TableName", importedfilepath); //more than 1 parameter so import a list here
As a result, my expected new mutated function would be like follows:
Note that the code below it's not correct but I am trying to replicate my intuition so you can understand what I am trying to do. The code below will help you to understand what I am trying to achieve on high level.
public static void LoadFiles(string connectionString, string importedfilepath, string ProcedureName, list ParameterName, list ParameterValue)
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
foreach (string parametername in ParameterName)
{
foreach (string parametervalue in ParameterValue)
{
command.Parameters.AddWithValue(parametername, parametervalue ); //Generate as many as the length of the two lists
}
}
command.ExecuteNonQuery();
if (command.ExecuteNonQuery() > 0)
MessageBox.Show("Selected file was loaded successfully");
}
}
}
When calling the method from a LoadButton1_Click
private void LoadButton1_Click(object sender, RoutedEventArgs e)
{
ParameterNamelist = list ["#TableName", "#Username", "#Email"]
ParameterValuelist = list [importedfilepath, "Nikos", "nikos#stackoverflow.com"]
var connectionString = SQLServerConnectionDetails();
LoadFiles(connectionString, FileNameTextBox.Text, "SelectUsernameProcedure", ParameterNamelist, ParameterValuelist);
}
When calling the exact method from a LoadButton2_Click
private void LoadButton2_Click(object sender, RoutedEventArgs e)
{
ParameterNamelist = list ["#TableName", "#ProductName"]
ParameterValuelist = list [importedfilepath, "Choco"]
var connectionString = SQLServerConnectionDetails();
LoadFiles(connectionString, FileNameTextBox.Text, "SelectProductProcedure", ParameterNamelist, ParameterValuelist);
}
You can use a Dictionary instead:
public static bool LoadFiles(string connectionString, string ProcedureName,
IReadOnlyDictionary<string, object> parameters)
{
using var sqlConnection = new SqlConnection(connectionString);
sqlConnection.Open();
using var command = sqlConnection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
foreach (KeyValuePair<string, object> parameter in parameters)
{
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
return (command.ExecuteNonQuery() > 0);
}
and then
private void LoadButton1_Click(object sender, RoutedEventArgs e)
{
var parameters = new Dictionary<string, object>()
{
["#TableName"] = importedfilepath,
["#Username"] = "Nikos",
["#Email"] = "nikos#stackoverflow.com"
};
var connectionString = SQLServerConnectionDetails();
bool commandExecuted = LoadFiles(connectionString,
"SelectUsernameProcedure", parameters);
if (commandExecuted)
{
MessageBox.Show("Selected file was loaded successfully");
}
}
You could pass a Dictionary<string, object> into the method instead of two separate lists:
Example Dictionary:
Dictionary<string, object> myparams = new Dictionary<string, object>()
{
{"#TableName", importedfilepath },
{"#ProductName", "Choco" }
};
New Method:
public static void LoadFiles(string connectionString, string ProcedureName,
Dictionary<string, object> #params)
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
foreach (string key in #params.Keys)
{
command.Parameters.AddWithValue(key, #params[key]);
}
command.ExecuteNonQuery();
if (command.ExecuteNonQuery() > 0)
MessageBox.Show("Selected file was loaded successfully");
}
}
}
Since you state you want to use your pseudo code from above, with the two lists, then it would work as so, I would suggest using the Dictionary approach provided above, but to satisfy your question:
//This is not a very safe way
//1. Both lists would need to have the same Count
//2. Parameters names would need to be ordered the same as Parameter values
public static void LoadFiles(string connectionString, string ProcedureName,
List<string> ParameterName, List<object> ParameterValue)
{
if(ParameterName.Count != ParameterValue.Count)
throw new Exception("Lists are of different Count");
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
for(int i = 0; i < ParameterName.Count; i++)
{
command.Parameters.AddWithValue(ParameterName[i], ParameterValue[i]); //Generate as many as the length of the two lists
}
command.ExecuteNonQuery();
if (command.ExecuteNonQuery() > 0)
MessageBox.Show("Selected file was loaded successfully");
}
}
}

DataTable Always Returns 0

My query returns results, but for some reason my DataTable always shows 0. The only thing I altered was the fact that I added parameters to the C# syntax (altho if I manually run the stored procedure it returns results). This is my syntax, does anyone see something that is incorrect syntactically in it?
protected void btnPress1464()
{
RunSQLStoredProc();
DataTable tableA = ebdb.Tables[0];
if (this.dtgAttendanceTracker.Items.Count == 0)
{
this.gvwTest.DataSource = tableA
this.gvwTest.DataBind();
}
}
public DataSet RunSQLStoredProc()
{
ebdb = new DataSet();
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
return ebdb;
}
public DataSet DoThis(string sqlQuery, int employeeid, DateTime hiredate, DateTime terminationdate)
{
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (SqlConnection conn = new SqlConnection(connstring.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = sqlQuery;
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#employeeid", employeeid.ToString());
cmd.Parameters.AddWithValue("#hiredate", hiredate.ToShortDateString());
cmd.Parameters.AddWithValue("#terminationdate", terminationdate.ToShortDateString());
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ebdb);
conn.Close();
}
}
return ebdb;
}
catch (Exception exception) { throw exception; }
}
The CommandText should only contain the stored-procedure name and not also exec if the command's CommandType is StoredProcedure. The StringBuilder is also redundant.
I also think that the way how you use AddWithValue with the wrong types could cause this issue(look at the last paragraph of my answer):
So not
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
but
ebdb = DoThis("alphadawg", otherParamaters...);
It's also bad practice to pass a sql-string to a method that executes it, that often introduces sql injection issues. You should not have a method DoThis but GetAlphaDawg which encapsulates the sql-query and only pass the parameter-values.
Apart from that, why do you return the DataSet from a method if it's actually a field in your class that you return? Instead initialize and fill it in the method, that's much clearer and also prevents issues when you load an already filled dataset(data will be appended by default).
This would be a possible implementation. Note that you shouldn't use AddWithValue and don't use String for DateTime but always use the correct type, all the more if you use AddWithValue which needs to infer the type from the value:
public DataSet GetAlphaDawg(int employeeid, DateTime hiredate, DateTime terminationdate)
{
DataSet dsAlpha = new DataSet();
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (var conn = new SqlConnection(connstring.ConnectionString))
{
using (var da = new SqlDataAdapter("alphadawg", conn))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
var parameter = da.SelectCommand.Parameters;
parameter.Add("#employeeid", SqlDbType.Int).Value = employeeid;
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate;
parameter.Add("#terminationdate", SqlDbType.Date).Value = terminationdate;
da.Fill(dsAlpha); // Open/Close not needed with Fill
return dsAlpha;
}
}
} catch (Exception) { throw; }
}
Since you use ToShortDateString, if you actually want to remove the time portion of your DateTime use DateTime.Date, for example:
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate.Date;

DataGrid - SQL insert query error c#

I'm trying to adding to table access and datagrid row with sql query. buy without a success. Any ideas ? Thank's
My sql query :
DataBaseIkuns.Instance.InsertToDB(string.Format(DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], o.ID_Observer, o.Lat, o.Long, o.azimuth));
public static Dictionary<CommendTypes, string> dictioneary = new Dictionary<CommendTypes, string>
{
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)"
+"values('{0}','{1}','{2}','{3}')"},
{CommendTypes.AzimuthLongLatFromOB,"SELECT ID_Observer,Longitude,Latitude,Azimuth FROM Observer Where ID_Observer = {0}"}
};
public void InsertToDB(string sql) // It get the right values - 1,2,3,4
{
int insert = 0;
try
{
if (con.State.ToString()== "Open")
{
cmd = new OleDbCommand();
oledbAdapter = new OleDbDataAdapter();
dt = new DataTable();
cmd.Connection = con;
cmd.CommandText = sql;
insert = cmd.ExecuteNonQuery(); // Here it jump's to the catch. why ?
if (insert > 0)
{
MessageBox.Show("Your Insert successed");
}
else
{
MessageBox.Show("Your Insert failed");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Have a comment in the code where the program falls.
The value of sql when it falls :
Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)values('3','31.4','34','150')
If you use an Access Database behind the OleDb provider then you have a problem with the word LONG. It is a reserved keyword (and probably it is the same in many other database systems). In this case you need to encapsulate the field name in square brackets
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,[Long],Azimuth)"
Said that you need to start to use parameterized query. Your string.Format is another kind of string concatenation that leads to Sql Injection, parsing problems and subtle syntax error when you miss a single quote or other type specifier in your query string
For example
public static Dictionary<CommendTypes, string> dictioneary = new Dictionary<CommendTypes, string>
{
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)"
+"values(?,?,?,?)"},
{CommendTypes.AzimuthLongLatFromOB,"SELECT ID_Observer,Longitude,Latitude,Azimuth "
+"FROM Observer Where ID_Observer = ?"}
};
public void InsertToDB(string sql, List<OleDbParameter> parameters)
{
int insert = 0;
try
{
if (con.State.ToString()== "Open")
{
using(cmd = new OleDbCommand());
{
cmd.Connection = con;
cmd.CommandText = sql;
cmd.Parameters.AddRange(parameters.ToArray());
insert = cmd.ExecuteNonQuery();
}
........
}
}
......
}
Now when you call the InsertDB you write
DataBaseIkuns.Instance.InsertToDB(string.Format(DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], , o.Lat, o.Long, o.azimuth));
List<OleDbParameter> parameters = new List<OleDbParameter>();
parameters.Add(new OleDbParameter())
{
ParameterName = "#p1", OleDbType= OleDbType.VarWChar, Value = o.ID_Observer
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p2", OleDbType= OleDbType.VarWChar, Value = o.Lat
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p3", OleDbType= OleDbType.VarWChar, Value = o.Long
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p4", OleDbType= OleDbType.VarWChar, Value = o.Azimuth
}
DataBaseIkuns.Instance.InsertToDB(
DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], parameters);

Parameterized SQL in C# .Net

If I have code like so:
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, params IDataParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
new SqlParameter("dataId", dataId));
}
}
How do I ensure that the
new SqlParameter("dataId", dataId));
piece of code is passing in a text or maybe an integer? Also how does the #"select..." actually work as I'm familiar to:
select id from mytable where dataId = #dataID;
I'm not sure that's parameterized properly. Take a look at the below modified code that will ensure the parameters are added properly and simplify the construction of the call to ExecuteQuery (in my opinion of course). This is pretty straight forward. The select statement is parameterized properly because it's using the #varname syntax:
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId"
Further, the parameters are typed properly because of the AddWithValue method:
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
Finally, using the dictionary to send in the parameters keeps it pretty simple to construct the parameters from any structure, whether that be parameter values, or even an object.
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, Dictionary<string, object> parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId",
new Dictionary<string, object>() { { "#dataId", dataId } });
}
P.S. - the # before the string in your example is just an escape sequence used in C#.

Adding parameters to IDbCommand

I am creating a small helper function to return a DataTable. I would like to work across all providers that ADO.Net supports, so I thought about making everything use IDbCommand or DbCommand where possible.
I have reached a stumbling block with the following code:
private static DataTable QueryImpl(ref IDbConnection conn, String SqlToExecute, CommandType CommandType, Array Parameters)
{
SetupConnection(ref conn);
// set the capacity to 20 so the first 20 allocations are quicker...
DataTable dt = new DataTable();
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = SqlToExecute;
cmd.CommandType = CommandType;
if (Parameters != null && Parameters.Length > 0)
{
for (Int32 i = 0; i < Parameters.Length; i++)
{
cmd.Parameters.Add(Parameters.GetValue(i));
}
}
dt.Load(cmd.ExecuteReader(), LoadOption.OverwriteChanges);
}
return dt;
}
When this code is executed, I receive an InvalidCastException which states the following:
The SqlParameterCollection only accepts non-null SqlParameter type objects, not String objects.
The code falls over on the line:
cmd.Parameters.Add(Parameters.GetValue(i));
Any ideas?
Any improvements to the above code is appreciated.
Actual solution:
private static readonly Regex regParameters = new Regex(#"#\w+", RegexOptions.Compiled);
private static DataTable QueryImpl(ref DbConnection conn, String SqlToExecute, CommandType CommandType, Object[] Parameters)
{
SetupConnection(ref conn);
DataTable dt = new DataTable();
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = SqlToExecute;
cmd.CommandType = CommandType;
if (Parameters != null && Parameters.Length > 0)
{
MatchCollection cmdParams = regParameters.Matches(cmd.CommandText);
List<String> param = new List<String>();
foreach (var el in cmdParams)
{
if (!param.Contains(el.ToString()))
{
param.Add(el.ToString());
}
}
Int32 i = 0;
IDbDataParameter dp;
foreach (String el in param)
{
dp = cmd.CreateParameter();
dp.ParameterName = el;
dp.Value = Parameters[i++];
cmd.Parameters.Add(dp);
}
}
dt.Load(cmd.ExecuteReader(), LoadOption.OverwriteChanges);
}
return dt;
}
Thanks for ideas/links etc. :)
I believe IDbCommand has a CreateParameter() method:
var parameter = command.CreateParameter();
parameter.ParameterName = "#SomeName";
parameter.Value = 1;
command.Parameters.Add(parameter);
You could add the code of the accepted answer to an extension method:
public static class DbCommandExtensionMethods
{
public static void AddParameter (this IDbCommand command, string name, object value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.Value = value;
command.Parameters.Add(parameter);
}
}
I know it's not what you're asking, but I have a much simpler and more robust solution to offer.
The Microsoft Patterns and Practices library includes a Data Access Application block that is incredibly powerful and easy to use. A sample for executing a stored procedure and returning a dataset is shown below from our actual code:
object[] ParameterValues = new object[] {"1",DateTime.Now, 12, "Completed", txtNotes.Text};
Database db = DatabaseFactory.CreateDatabase("ConnectionStringName");
DataSet ds = = db.ExecuteDataSet("StoredProcName", ParameterValues);
It doesn't matter if the Connection is OleDb, ODBC, etc. The ConnectionStringName in the first line of code is just the name of the Consternating as defined in the .config file. You pass in a Connection String name, stored proc name, and an array of objects, which make up the parameters.
This is just one of the many sweet functions available.
You'll get everything you're trying to build and then some.
The official site is here: http://msdn.microsoft.com/en-us/library/ff648951.aspx
To save you some searching, the Data classes documentation are found here: http://msdn.microsoft.com/en-us/library/microsoft.practices.enterpriselibrary.data(PandP.50).aspx
(and it's free from Microsoft, and updated regularly.)
This answer is intended for slightly more specific purpose than what you're doing, but building on #Dismissile's answer, I used a Dictionary to supply the parameter name and value to a foreach loop in my personal project.
using( IDbCommand dbCommand = dbConnection.CreateCommand() )
{
dbCommand.CommandText = Properties.Settings.Default.UpdateCommand;
Dictionary<string,object> values = new Dictionary<string,object>()
{
{"#param1",this.Property1},
{"#param2",this.Property2},
// ...
};
foreach( var item in values )
{
var p = dbCommand.CreateParameter();
p.ParameterName = item.Key;
p.Value = item.Value;
dbCommand.Parameters.Add(p);
}
}
Your Parameters parameter needs to be of type IDataParameter[] and, given the error text, the concrete implementation needs be a SqlParameter[] type.
If you wish to keep your signature, you'll need a factory to derive the necessary concrete implementation.
Add using System.Data.SqlClient; and
cmd.Parameters.Add(new SqlParameter("#parameterName", value));

Categories

Resources