How to pass sqlParameterCollection to Command Object - c#

I have a Command object like this:
SqlCommand cmd1 = new SqlCommand();
How can I build the sqlParameterCollection in another routine and pass this collection to this Command object?
I have this code:
private SqlParameterCollection BuildParameters()
{
SqlCommand cmd2 = new SqlCommand();
cmd2.Parameters.Add("#year", SqlDbType.Int).Value = 2013;
return cmd2.Parameters;
}
I would like to do something like this:
cmd1.Parameters = BuildParameters();

The SqlCommand.Parameters property is read only so you cannot assign an SqlParameterCollection object to it.
You may want to return an SqlParameter array from BuildParameters thus:
private SqlParameter[] BuildParameters()
{
SqlParameter[] para = new SqlParameter[3];
para[0] = new SqlParameter("#year", SqlDbType.Int) { Value = 2013 };
return para;
}
Then you can do the following:-
SqlCommand cmd1 = new SqlCommand();
cmd1.Parameters.Clear();
cmd1.Parameters.AddRange(BuildParameters());

you could handle this as an extension method, but honestly I don't know really how useful this would be
public static IEnumerable<IDbDataParameter> BuildParameters(this IDbCommand command, params KeyValuePair<string, DbType>[] parameters)
{
foreach(KeyValuePair<string, DbType> kvpParam in parameters)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = kvpParam.Key;
param.DbType = kvpParam.Value;
yield return param;
}
}

You can simple iterate throught the collection.
foreach (var mysqlParamater in oMySqlParameterCollection)
{
oMySqlCommand.Parameters.Add(mysqlParamater);
}
I have written a method that accept mysqlparamater collection along with SP name you can use it as below:-
public int ExecuteNonQuery(MySqlParameterCollection oMySqlParameterCollection, string storedProcedure)
{
int affectedRows = 0;
_oMySqlConnection.Open();
MySqlCommand oMySqlCommand = new MySqlCommand
{
CommandType = CommandType.StoredProcedure,
CommandText = storedProcedure,
Connection = _oMySqlConnection
};
foreach (var mysqlParamater in oMySqlParameterCollection)
{
oMySqlCommand.Parameters.Add(mysqlParamater);
}
affectedRows = oMySqlCommand.ExecuteNonQuery();
_oMySqlConnection.Close();
return affectedRows;
}

Related

Read data from DB generic function

I am new to working in dot net. What I am trying to do is create a helper method which will fetch data from DB for any table. I have passed parameters dynamically it working fine. But when I try to read the data, I am finding it difficult to store the data in some collection. This I will be returning back to my calling point and bind it to a response type and return.
public static Dictionary<Dictionary<string, object>, object> GetData(SqlCommand cmd, string connectionString, List<SqlParameter> parameters)
{
try
{
SqlDataReader reader = null;
Dictionary<Dictionary<string,object>, object> returnObjects = new Dictionary<Dictionary<string, object>, object>();
Dictionary<string, object> returnObject = new Dictionary<string, object>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
returnObject.Add(reader.GetName(i), reader[i]);
}
returnObject.Clear();
returnObjects.Add(returnObject, reader);
}
}
}
return returnObjects;
}
catch (Exception ex)
{
throw;
}
}
In above code when i try to add in returnObjects dictionary it says key already added. Below is the code from where I am calling and patientResponse where I want to return.
Dictionary<Dictionary<string, object>, object> dct = new Dictionary<Dictionary<string, object>, object>();
dct = Helper.GetData(cmd, connectionString, parameters);
List<Patient_Response> pp = new List<Patient_Response>();
Patient_Response pr = new Patient_Response();
pr.Patient_Id = int.Parse(reader["ID"].ToString());
pr.FIRST_NAME = reader["FIRST_NAME"].ToString();
pr.LAST_NAME = reader["LAST_NAME"].ToString();
pr.phoneNumber = reader["TEL"].ToString();
pr.email = reader["EMAIL"].ToString();
pr.Address = reader["Address"].ToString();
pr.Gender = reader["Gender"].ToString();
pr.DOB = Convert.ToDateTime(reader["DOB"]).ToString("MM/dd/yyyy");
pp.Add(pr);
What i can use instead of dictionary so that i can get a collection returned.
What surely doesn't work in your code is that you add the same instance of returnObject for each row. You should put new Dictionary<string, object>() inside the while. Also, if your result set contains duplicate field names, you'll get an exception.
public static List<Dictionary<string, object>> GetData(SqlCommand cmd, string connectionString, List<SqlParameter> parameters)
{
try
{
SqlDataReader reader = null;
// changed this to be a list
List<Dictionary<string,object>> returnObjects = new List<Dictionary<string, object>>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
// create a new returnObject for each row
Dictionary<string, object> returnObject = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
// following line throws an exception if there are multiple fields with the same name
returnObject.Add(reader.GetName(i), reader[i]);
}
returnObjects.Add(returnObject);
}
}
}
return returnObjects;
}
catch (Exception ex)
{
throw;
}
}
That said, there is not much benefit from first putting the content of reader into a list of dictionaries and then into a list of Patient_Response. You might as well directly fill a List<Patient_Response> from the reader (i.e. inside the while).
A generic way to do this might be (code is not verified to compile or run):
public static List<T> GetData<T>(
SqlCommand cmd,
string connectionString,
List<SqlParameter> parameters,
Func<IDataReader, T> readerToRow)
{
List<T> returnObjects = new List<T>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
returnObjects.Add(readerToRow(reader));
}
}
return returnObjects;
}
// To call above function:
List<Patient_Response> pp = Helper.GetData(
cmd,
connectionString,
parameters,
reader =>
new Patient_Response
{
FIRST_NAME = reader["FIRST_NAME"].ToString(),
LAST_NAME = reader["LAST_NAME"].ToString(),
// more columns go here
}
);

SQL Server stored procedure complains about missing parameter, but it is being set

I have this method:
public class StoredProcedureProvider : IStoredProcedureProvider
{
private readonly string _connectionString;
public StoredProcedureProvider(IDataConfig config) => _connectionString = config.ConnectionString;
public T ExecuteScalar<T>(string procedureName, IList<SqlParameter> parameters = null)
{
return Execute(procedureName, parameters, (connection, command) =>
{
connection.Open();
var result = command.ExecuteScalar();
return (T) Convert.ChangeType(result, typeof(T));
});
}
public DataSet GetDataSet(string procedureName, IList<SqlParameter> parameters = null)
{
return Execute(procedureName, parameters, (connection, command) =>
{
var dataAdapter = new SqlDataAdapter();
var dataSet = new DataSet();
dataAdapter.SelectCommand = command;
dataAdapter.Fill(dataSet);
return dataSet;
});
}
public DataRow GetDataRow(string procedureName, IList<SqlParameter> parameters = null)
{
var dataSet = GetDataSet(procedureName, parameters);
var tables = dataSet.Tables.Cast<DataTable>().ToList();
if (!tables.Any())
throw new Exception("The DataSet contains not tables");
var dataRows = dataSet.Tables[0].Rows.Cast<DataRow>().ToList();
if (!dataRows.Any())
throw new Exception("The DataTable contains no rows");
var dataRow = dataRows.First();
return dataRow;
}
private static T Execute<T>(string procedureName, IList<SqlParameter> parameters, Func<SqlConnection, SqlCommand, T> returnMethod)
{
using (var connection = new SqlConnection(_connectionString))
{
using (var command = new SqlCommand(procedureName, connection))
{
command.CommandType = CommandType.StoredProcedure;
if (parameters == null || !parameters.Any())
return returnMethod(connection, command);
foreach (var parameter in parameters)
command.Parameters.Add(parameter);
return returnMethod(connection, command);
}
}
}
}
When I invoke anything with a parameter that is not a string, I get an error:
Procedure or function 'generalLogonFromSessionID' expects parameter '#sessionID', which was not supplied.
The code I am using to test this is:
var dataRow = _storedProcedureProvider.GetDataRow("generalLogonFromSessionID", new List<SqlParameter>
{
new SqlParameter("#sessionID", SqlDbType.UniqueIdentifier, oTokenData.TokenId.Length, oTokenData.TokenId),
new SqlParameter("#ipAddress", ipAddress)
});
But if I use a stored procedure that only accepts string parameters, it works fine.
For example:
var dataRow = _storedProcedureProvider.GetDataRow("generalLogon", new List<SqlParameter>
{
new SqlParameter("#loginName", oTokenData.Update.User.EmailAddress),
new SqlParameter("#password", bookingType == BookingType.Commercial ? lastName : "password"),
new SqlParameter("#ipAddress", ipAddress)
});
Does anyone know why?
Current example is using the wrong constructor overload.
public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn);
Update to use the following
new SqlParameter("#sessionID", SqlDbType.UniqueIdentifier) {
Value = oTokenData.TokenId
};
You supplied the #session parameter, but I don't see you set any value for it..
new SqlParameter("#sessionID", SqlDbType.UniqueIdentifier) { Value = oTokenData.TokenId }
Incidentally, totally appreciate that you're trying to make you life easier by creating a class here that does lots of DB donkey work and means your code becomes simpler.. but it still looks easy to make a mistake (like this). Perhaps take a look at Dapper; it would make the code to exec your proc look more like:
using (var connection = someSqlConnection)
{
var result = connection.Execute("generalLogonFromSessionID",
new {
sessionID = oTokenData.TokenId,
ipAddress = ipAddress
},
commandType: CommandType.StoredProcedure
);
//do something with result
}
No affiliation, just think it's an awesome library that is like what you're trying to achieve, but goes further.. No point reinventing the wheel?

Passing multiple SqlParameter to method

In my main form, I have implemented this code..
void SampleMethod(string name, string lastName, string database)
{
SqlParameter sqlParam = new SqlParameter();
sqlParam.ParameterName = "#name";
sqlParam.Value = name;
sqlParam.SqlDbType = SqlDbType.NVarChar;
SqlParameter sqlParam1 = new SqlParameter();
sqlParam1.ParameterName = "#lastName";
sqlParam1.Value = lastName;
sqlParam1.SqlDbType = SqlDbType.NVarChar;
SqlParameter sqlParam2 = new SqlParameter();
sqlParam2.ParameterName = "#database";
sqlParam2.Value = database;
sqlParam2.SqlDbType = SqlDbType.NVarChar;
SampleClass sampleClass = new SampleClass(new DBConn(#serverName, tableName, userName, password));
sampleClass.executeStoredProc(dataGridView1, "sp_sampleStoredProc", sqlParam, sqlParam1, sqlParam2);
}
And in my SampleClass, I have this kind of method.
public DataGridView executeStoredProc(DataGridView dtgrdView, string storedProcName, params SqlParameter[] parameters)
{
try
{
DataTable dt = new DataTable();
sqlDA = new SqlDataAdapter(storedProcName, sqlconn);
sqlDA.SelectCommand.CommandType = CommandType.StoredProcedure;
sqlDA.SelectCommand.CommandTimeout = 60;
// Loop through passed parameters
if (parameters != null && parameters.Length > 0)
{
foreach (var p in parameters)
sqlDA.SelectCommand.Parameters.Add(p);
}
sqlDA.Fill(dt);
dtgrdView.DataSource = dt;
sqlconn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
sqlconn.Close();
}
return dtgrdView;
}
What I am trying to do is avoid multiple
SqlParameter sqlParam = new SqlParameter()
in my code, I have tried so many solutions for this problem but I didn't get the right answer. I have also tried to research about this but I still couldn't get the right answer.
Please don't mind my naming convention and other codes as I intentionally change many of them :) Thanks.
As an alternative to your solution, try to use already existing one instead, using Dapper (https://github.com/StackExchange/dapper-dot-net).
You still need to use multiple parameters if your stored procedure or query needs it, but this is nicely abstracted for you and this will definatelly reduce the amount of code.
void SampleMethod(string name, string lastName, string database)
{
using(var connection = new SqlConnection(MY_CONNECTION_STRING))
{
var resultListOfRows = connection.Query<ReturnObject>(MY_STORED_PROCEDURE, new {
name = name,
lastName = lastName,
database = database}, commandType: System.Data.CommandType.StoredProcedure);
}
}
First of all, the easiest option would be to use a microORM like Dapper, and retrieve a strongly-typed collection. Gridviews can bind to anything, including strongly typed collections. All this code could become:
using(var con=new SqlConnection(myConnectionString))
{
con.Open();
var result= connection.Query<ResultClass>("sp_MySproc",
new { Name= name, LastName= lastName,Database=database},
commandType: CommandType.StoredProcedure);
return result;
}
Even when using raw ADO.NET, you can create a SqlParameter in one line by using the appropriate constructor . For example, you can create a new nvarchar(n) parameter with:
var myParam=new SqlParameter("#name",SqlDbType.NVarchar,20);
or
var myParam=new SqlParameter("#name",SqlDbType.NVarchar,20){Value = name};
A better idea though is to create the SqlCommand object just once and reuse it. Once you have an initialized SqlCommand object, you can simply set a new connection to it and change the parameter values, eg:
public void Init()
{
_loadCustomers = new SqlCommand(...);
_loadCustomers.Parameters.Add("#name",SqlDbType.NVarChar,20);
...
}
//In another method :
using(var con=new SqlConnection(myConnectionString)
{       
_loadCustomers.Connection=con;
_loadCustomers.Parameters["#name"].Value = myNameParam;
con.Open();
using(var reader=_load.ExecuteReader())
{
//...
}
}
You can do the same thing with a SqlDataAdapter, in fact that's how Windows Forms and Data Adapters are meant to be used since .NET 1.0 .
Instead of creating a new one each time you want to fill your grid, create a single one and reuse it by setting the connection and parameters before execution. You can use the SqlDataAdapter(SqlCommand) constructor to make things a bit cleaner:
public void Init()
{
_loadCustomers = new SqlCommand(...);
_loadCustomers.Parameters.Add("#name",SqlDbType.NVarChar,20);
....
_myGridAdapter = new SqlDataAdapter(_loadCustomers);
...
}
And call it like this:
using(var con=new SqlConnection(myConnectionString))
{
_myGridAdapter.SelectCommand.Connection=con;
_myGridAdapter.SelectCommand.Parameters["#name"].Value =....;
con.Open();
var dt = new DataTable();
_myGridAdapter.Fill(dt);
dtgrdView.DataSource = dt;
return dtgrdView;
}
Separate your Database logic at one place(put sqladapter, sqlcommand etc at one place), Then encapsulate parameters within your command like mentioned below and you don't need to declare sqlparameter separately, add it inside parameters list.
cmdToExecute.Parameters.Add(new SqlParameter("#parameter", value));
Take a look at the complete example below
public DataTable ProdTypeSelectAll(string cultureCode)
{
SqlCommand cmdToExecute = new SqlCommand();
cmdToExecute.CommandText = "dbo.[pk_ProdType_SelectAll]";
cmdToExecute.CommandType = CommandType.StoredProcedure;
DataTable toReturn = new DataTable("ProdType");
SqlDataAdapter adapter = new SqlDataAdapter(cmdToExecute);
cmdToExecute.Connection = _mainConnection;
cmdToExecute.Parameters.Add(new SqlParameter("#CultureName", cultureCode));
_mainConnection.Open();
adapter.Fill(toReturn);
return toReturn;
}
You may be able to use the SqlParameter Constructor (String, Object). Replace:
sampleClass.executeStoredProc(dataGridView1,
"sp_sampleStoredProc",
sqlParam,
sqlParam1,
sqlParam2);
With:
sampleClass.executeStoredProc(dataGridView1,
"sp_sampleStoredProc",
new SqlParameter("#name", (object)name),
new SqlParameter("#lastName", (object)lastName),
new SqlParameter("#database", (object)database));

Adding multiple parameters to a query

So I wish to create a nice function that will query MySQL commands in a nicer way. Something along the lines of this one:
public int mysql_query_scalar(string query, parameters)
{
mysql_Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = mysql_connection;
cmd.CommandText = query;
cmd.Prepare();
cmd.Parameters.AddWithValue(parameterfromstring, parameternumber);
mysql_Close();
return int.Parse(cmd.ExecuteScalar().ToString());
}
Now, what I wish to do is to be able to add as many parameters as I can, and then start building up parameters. How is this possible? If so, please tell me or give me a snippet of it please.
I will gladly appreciate someone telling me how to use it too. I am pretty new to MySQL and I am not C# expert.
mysql_Open(); and mysql_Close(); are functions I did myself. Refer them as mysql_connection.Open(); and mysql_connection.Close();
You can use an extension method, like this:
public static class DbCommandExtensions
{
public static void AddInputParameters<T>(this IDbCommand cmd,
T parameters) where T : class
{
foreach (var prop in parameters.GetType().GetProperties())
{
object val = prop.GetValue(parameters, null);
var p = cmd.CreateParameter();
p.ParameterName = prop.Name;
p.Value = val ?? DBNull.Value;
cmd.Parameters.Add(p);
}
}
}
Call as below :
cmd.AddInputParameters(new { a = textBox1.Text, b = TextBox2.Text.... });
i would call this method that way:
List<SqlParameter> sList = new List<SqlParameter>();
sList.Add(new SqlParameter("#myValue", myValue));
sList.Add(new SqlParameter("#myValue2", myValue2));
mysql_query_scalar(myQuery, sList.ToArray());
and i would use using-directives
public static int mysql_query_scalar(string Command, SqlParameter[] parameters)
{
using (MySqlConnection myConnection = new MySqlConnection(ConnectionString))
{
myConnection.Open();
using (MySqlCommand myCommand = new MySqlCommand(Command, myConnection))
{
myCommand.Parameters.AddRange(parameters); // Add Parameters here
return (int)myCommand.ExecuteScalar();
}
}
}

Sql Parameter Collection

I have 5 parameters and I want to send them to the method:
public static SqlCommand getCommand(string procedure, SqlParameter[] parameter)
{
Sqlcommand cmd;
return cmd
}
Can I send these paramters at one time like this?
SqlParameterCollection prm;
prm.Add(p1);
prm.Add(p2);
prm.Add(p3);
prm.Add(p4);
prm.Add(p5);
sqlcommand cmd = getCommand(prm);
Or create an array of parameters by hand:
SqlParameter[] parameter = {
new SqlParameter(...),
new SqlParameter(...),
new SqlParameter(...)
};
But I don't see what should be wrong with your approach. It simple, readable and understendable.
I don't see what's wrong with that? You do know that, if this is .NET, you need to attach the parameters to the SqlCommand object?
So:
SqlCommand query = new SqlCommand(sqlString, Connection);
query.Parameters.AddWithValue(parameter,valueToPass);
etc?
Sorry if that's not related, not completely sure on your question? Your method doesn't really do anything, I take you left out the code and just put in a dummy for the purposes of asking the question? You can pass an array as an arguement so you just need to spilt it up?
Using this as inspiration, this code worked for me:
List<SqlCeParameter> parameters = new List<SqlCeParameter>();
parameters.Add(new SqlCeParameter("#Username", NewUsername));
parameters.Add(new SqlCeParameter("#Password", Password));
cmd.Parameters.AddRange(parameters.ToArray());
Well, that won't compile because in your call to getCommand you're not passing in a string with the procedure, but as far as the array, that should work no problem.
Here is my code.You can use all parameter properties like name,value,type etc.
int SelectedListID = 6;
string selectedPrefix = "IP";
string sqlQuery = "select * from callHistory where ImportID=#IMPORTID and Prefix=#PREFIX"
SqlParameter[] sParams = new SqlParameter[2]; // Parameter count
sParams[0] = new SqlParameter();
sParams[0].SqlDbType = SqlDbType.Int;
sParams[0].ParameterName = "#IMPORTID";
sParams[0].Value = SelectedListID;
sParams[1] = new SqlParameter();
sParams[1].SqlDbType = SqlDbType.VarChar;
sParams[1].ParameterName = "#PREFIX";
sParams[1].Value = selectedPrefix;
SqlCommand cmd = new SqlCommand(sqlQuery, sConnection);
if (sParams != null)
{
foreach (SqlParameter sParam in sParams)
{
cmd.Parameters.Add(sParam);
Application.DoEvents();
}
}
Form1.cs
static private void FunctionCall()
{
string connectionString = "DATA Source=nwind;server=GRAPHICS\SQLEXPRESS;Persist Security Info=False;Integrated Security=SSPI;Connect Timeout=30";
string sSqlQuery;
DataSet ds;
DataTable dt;
// Prepare SQL Query
sSqlQuery = #"
select content " +
"from " +
"[TBL] where id = '000-000'";
SqlParameter[] sqlParams = {
new SqlParameter("",SqlDbType.Int),
new SqlParameter("",SqlDbType.VarChar),
new SqlParameter("",SqlDbType.VarChar)
};
// Read from database
ds = SqlHelper.ExecuteNonQuery(connectionString, sSqlQuery, CommandType.Text, sqlParams);
dt = ds.Tables[0];
}
SqlHelper.cs
// Executes a non query
public static int ExecuteNonQuery (string connectionString, string cmdText, CommandType type, SqlParameter[] prms)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand(cmdText, conn))
{
cmd.CommandType = type;
if (prms != null)
{
foreach (SqlParameter p in prms)
{
cmd.Parameters.Add(p);
}
}
conn.Open();
return cmd.ExecuteNonQuery();
}
}
}
1.
public IEnumerable<SqlParameter> GetAndSetParameters(List<Tuple<string, string>> parameters){
List<SqlParameter> paramlist = new List<SqlParameter>();
foreach (var item in parameters)
{
paramlist.Add(new SqlParameter(item.Item1, item.Item2));
}
return paramlist;
}
2. pass parameters
public List<Tuple<string, string>> GetUserParameter(){
List<Tuple<string, string>> list = new List<Tuple<string, string>>();
list.Add(new Tuple<string, string>("#User",user.UserID));
return list;
}
3. finally use it:
SqlCommand oCmd = new SqlCommand(oString, myConnection);
oCmd.Parameters.AddRange(GetAndSetParameters(GetUserParameter()).ToArray());

Categories