How to dynamically name a variable - c#

I would like to dynamically set string variables taken from a database.
So instead of setting each variable manually as bellow
String XXX = "123";
String YYY = "456";
String ZZZ = "789";
I would like to set the variables dynamical as follows
Table: DefualtValues
ID
Value
XXX
123
YYY
456
ZZZ
789
myDBConnection.Open();
string GetDefualtValuesSQL = "SELECT * FROM DefualtValues";
SQLCommand = new MySqlCommand(GetDefualtValuesSQL, myDBConnection);
dbReader = SQLCommand.ExecuteReader();
while (dbReader.Read())
{
String dbReader.GetString(0)= dbReader.GetString(1);
}
myDBConnection.Close();
Console.WriteLine(XXX);
/// Should output 123

As people in the comments mentioned, you can go for something like this:
myDBConnection.Open();
string GetDefualtValuesSQL = "SELECT * FROM DefualtValues";
SQLCommand = new MySqlCommand(GetDefualtValuesSQL, myDBConnection);
dbReader = SQLCommand.ExecuteReader();
IDictionary<string, string> result = new Dictionary<string, string>();
while (dbReader.Read())
{
result.Add(dbReader.GetString(0), dbReader.GetString(1));
}
myDBConnection.Close();
Console.WriteLine(result["XXX"]);
/// Should output 123

Related

How to convert String into string array in C#? [duplicate]

I am trying to pass array parameter to SQL commnd in C# like below, but it does not work. Does anyone meet it before?
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
sqlComm.Parameters.Add("#Age", SqlDbType.NVarChar);
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
sb.Append(item.Text + ",");
}
}
sqlComm.Parameters["#Age"].Value = sb.ToString().TrimEnd(',');
You will need to add the values in the array one at a time.
var parameters = new string[items.Length];
var cmd = new SqlCommand();
for (int i = 0; i < items.Length; i++)
{
parameters[i] = string.Format("#Age{0}", i);
cmd.Parameters.AddWithValue(parameters[i], items[i]);
}
cmd.CommandText = string.Format("SELECT * from TableA WHERE Age IN ({0})", string.Join(", ", parameters));
cmd.Connection = new SqlConnection(connStr);
UPDATE: Here is an extended and reusable solution that uses Adam's answer along with his suggested edit. I improved it a bit and made it an extension method to make it even easier to call.
public static class SqlCommandExt
{
/// <summary>
/// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
/// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN ({paramNameRoot}))
/// </summary>
/// <param name="cmd">The SqlCommand object to add parameters to.</param>
/// <param name="paramNameRoot">What the parameter should be named followed by a unique value for each value. This value surrounded by {} in the CommandText will be replaced.</param>
/// <param name="values">The array of strings that need to be added as parameters.</param>
/// <param name="dbType">One of the System.Data.SqlDbType values. If null, determines type based on T.</param>
/// <param name="size">The maximum size, in bytes, of the data within the column. The default value is inferred from the parameter value.</param>
public static SqlParameter[] AddArrayParameters<T>(this SqlCommand cmd, string paramNameRoot, IEnumerable<T> values, SqlDbType? dbType = null, int? size = null)
{
/* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually.
* Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
* IN statement in the CommandText.
*/
var parameters = new List<SqlParameter>();
var parameterNames = new List<string>();
var paramNbr = 1;
foreach (var value in values)
{
var paramName = string.Format("#{0}{1}", paramNameRoot, paramNbr++);
parameterNames.Add(paramName);
SqlParameter p = new SqlParameter(paramName, value);
if (dbType.HasValue)
p.SqlDbType = dbType.Value;
if (size.HasValue)
p.Size = size.Value;
cmd.Parameters.Add(p);
parameters.Add(p);
}
cmd.CommandText = cmd.CommandText.Replace("{" + paramNameRoot + "}", string.Join(",", parameterNames));
return parameters.ToArray();
}
}
It is called like this...
var cmd = new SqlCommand("SELECT * FROM TableA WHERE Age IN ({Age})");
cmd.AddArrayParameters("Age", new int[] { 1, 2, 3 });
Notice the "{Age}" in the sql statement is the same as the parameter name we are sending to AddArrayParameters. AddArrayParameters will replace the value with the correct parameters.
I wanted to expand on the answer that Brian contributed to make this easily usable in other places.
/// <summary>
/// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
/// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN (returnValue))
/// </summary>
/// <param name="sqlCommand">The SqlCommand object to add parameters to.</param>
/// <param name="array">The array of strings that need to be added as parameters.</param>
/// <param name="paramName">What the parameter should be named.</param>
protected string AddArrayParameters(SqlCommand sqlCommand, string[] array, string paramName)
{
/* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually.
* Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
* IN statement in the CommandText.
*/
var parameters = new string[array.Length];
for (int i = 0; i < array.Length; i++)
{
parameters[i] = string.Format("#{0}{1}", paramName, i);
sqlCommand.Parameters.AddWithValue(parameters[i], array[i]);
}
return string.Join(", ", parameters);
}
You can use this new function as follows:
SqlCommand cmd = new SqlCommand();
string ageParameters = AddArrayParameters(cmd, agesArray, "Age");
sql = string.Format("SELECT * FROM TableA WHERE Age IN ({0})", ageParameters);
cmd.CommandText = sql;
Edit:
Here is a generic variation that works with an array of values of any type and is usable as an extension method:
public static class Extensions
{
public static void AddArrayParameters<T>(this SqlCommand cmd, string name, IEnumerable<T> values)
{
name = name.StartsWith("#") ? name : "#" + name;
var names = string.Join(", ", values.Select((value, i) => {
var paramName = name + i;
cmd.Parameters.AddWithValue(paramName, value);
return paramName;
}));
cmd.CommandText = cmd.CommandText.Replace(name, names);
}
}
You can then use this extension method as follows:
var ageList = new List<int> { 1, 3, 5, 7, 9, 11 };
var cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM MyTable WHERE Age IN (#Age)";
cmd.AddArrayParameters("Age", ageList);
Make sure you set the CommandText before calling AddArrayParameters.
Also make sure your parameter name won't partially match anything else in your statement (i.e. #AgeOfChild)
If you can use a tool like "dapper", this can be simply:
int[] ages = { 20, 21, 22 }; // could be any common list-like type
var rows = connection.Query<YourType>("SELECT * from TableA WHERE Age IN #ages",
new { ages }).ToList();
Dapper will handle unwrapping this to individual parameters for you.
If you are using MS SQL Server 2008 and above you can use table-valued parameters like described here
http://www.sommarskog.se/arrays-in-sql-2008.html.
1. Create a table type for each parameter type you will be using
The following command creates a table type for integers:
create type int32_id_list as table (id int not null primary key)
2. Implement helper methods
public static SqlCommand AddParameter<T>(this SqlCommand command, string name, IEnumerable<T> ids)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.TypeName = typeof(T).Name.ToLowerInvariant() + "_id_list";
parameter.SqlDbType = SqlDbType.Structured;
parameter.Direction = ParameterDirection.Input;
parameter.Value = CreateIdList(ids);
command.Parameters.Add(parameter);
return command;
}
private static DataTable CreateIdList<T>(IEnumerable<T> ids)
{
var table = new DataTable();
table.Columns.Add("id", typeof (T));
foreach (var id in ids)
{
table.Rows.Add(id);
}
return table;
}
3. Use it like this
cmd.CommandText = "select * from TableA where Age in (select id from #age)";
cmd.AddParameter("#age", new [] {1,2,3,4,5});
Since there is a method on
SqlCommand.Parameters.AddWithValue(parameterName, value)
it might be more convenient to create a method accepting a parameter (name) to replace and a list of values. It is not on the Parameters level (like AddWithValue) but on command itself so it's better to call it AddParametersWithValues and not just AddWithValues:
query:
SELECT * from TableA WHERE Age IN (#age)
usage:
sqlCommand.AddParametersWithValues("#age", 1, 2, 3);
the extension method:
public static class SqlCommandExtensions
{
public static void AddParametersWithValues<T>(this SqlCommand cmd, string parameterName, params T[] values)
{
var parameterNames = new List<string>();
for(int i = 0; i < values.Count(); i++)
{
var paramName = #"#param" + i;
cmd.Parameters.AddWithValue(paramName, values.ElementAt(i));
parameterNames.Add(paramName);
}
cmd.CommandText = cmd.CommandText.Replace(parameterName, string.Join(",", parameterNames));
}
}
I want to propose another way, how to solve limitation with IN operator.
For example we have following query
select *
from Users U
WHERE U.ID in (#ids)
We want to pass several IDs to filter users. Unfortunately it is not possible to do with C# in easy way. But I have fount workaround for this by using "string_split" function. We need to rewrite a bit our query to following.
declare #ids nvarchar(max) = '1,2,3'
SELECT *
FROM Users as U
CROSS APPLY string_split(#ids, ',') as UIDS
WHERE U.ID = UIDS.value
Now we can easily pass one parameter enumeration of values separated by comma.
Passing an array of items as a collapsed parameter to the WHERE..IN clause will fail since query will take form of WHERE Age IN ("11, 13, 14, 16").
But you can pass your parameter as an array serialized to XML or JSON:
Using nodes() method:
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
sb.Append("<age>" + item.Text + "</age>"); // actually it's xml-ish
sqlComm.CommandText = #"SELECT * from TableA WHERE Age IN (
SELECT Tab.col.value('.', 'int') as Age from #Ages.nodes('/age') as Tab(col))";
sqlComm.Parameters.Add("#Ages", SqlDbType.NVarChar);
sqlComm.Parameters["#Ages"].Value = sb.ToString();
Using OPENXML method:
using System.Xml.Linq;
...
XElement xml = new XElement("Ages");
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
xml.Add(new XElement("age", item.Text);
sqlComm.CommandText = #"DECLARE #idoc int;
EXEC sp_xml_preparedocument #idoc OUTPUT, #Ages;
SELECT * from TableA WHERE Age IN (
SELECT Age from OPENXML(#idoc, '/Ages/age') with (Age int 'text()')
EXEC sp_xml_removedocument #idoc";
sqlComm.Parameters.Add("#Ages", SqlDbType.Xml);
sqlComm.Parameters["#Ages"].Value = xml.ToString();
That's a bit more on the SQL side and you need a proper XML (with root).
Using OPENJSON method (SQL Server 2016+):
using Newtonsoft.Json;
...
List<string> ages = new List<string>();
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
ages.Add(item.Text);
sqlComm.CommandText = #"SELECT * from TableA WHERE Age IN (
select value from OPENJSON(#Ages))";
sqlComm.Parameters.Add("#Ages", SqlDbType.NVarChar);
sqlComm.Parameters["#Ages"].Value = JsonConvert.SerializeObject(ages);
Note that for the last method you also need to have Compatibility Level at 130+.
Just changing DbType might be enough:
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand
{
Connection = sqlCon,
CommandType = CommandType.Text,
CommandText = sqlCommand,
CommandTimeout = 300
};
var itens = string.Join(',', ddlAge.Items);
sqlComm.Parameters.Add(
new SqlParameter("#Age", itens)
{
DbType = DbType.String
});
Use .AddWithValue(), So:
sqlComm.Parameters.AddWithValue("#Age", sb.ToString().TrimEnd(','));
Alternatively, you could use this:
sqlComm.Parameters.Add(
new SqlParameter("#Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar }
);
Your total code sample will look at follows then:
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
sb.Append(item.Text + ",");
}
}
sqlComm.Parameters.AddWithValue("#Age", sb.ToString().TrimEnd(','));
// OR
// sqlComm.Parameters.Add(new SqlParameter("#Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar });
Overview: Use the DbType to set the parameter type.
var parameter = new SqlParameter();
parameter.ParameterName = "#UserID";
parameter.DbType = DbType.Int32;
parameter.Value = userID.ToString();
var command = conn.CreateCommand()
command.Parameters.Add(parameter);
var reader = await command.ExecuteReaderAsync()
try it like this
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
sqlComm.Parameters.Add("#Age", SqlDbType.NVarChar);
sb.Append(item.Text + ",");
sqlComm.Parameters["#Age"].Value = sb.ToString().TrimEnd(',');
}
}

Select multiple values with same ID

Hi i am not sure how to retrieve all values with same id and display them in one label.
Example :
id food
1 chicken
1 fish
Desired output:
chicken,fish
How can i do that?
SqlConnection con1 = new SqlConnection(strConnString);
con.Open();
str = "select food from foodName where id= 1";
com = new SqlCommand(str, con);
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
Label1.Text = reader["food"].ToString();
}
con.Close();
Label1.Text = string.Empty;
SqlConnection con1 = new SqlConnection(strConnString);
con.Open();
str = "select food from foodName where id= 1";
com = new SqlCommand(str, con);
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
Label1.Text += reader["food"].ToString();
}
con.Close();
You can just do a foreach and add form a string using StringBuilder,
StringBuilder test = new StringBuilder();
foreach(yourClassname test in yourList)
{
test.Append(test.name);
}
Use lambda expression in C#.
var list = new List<Product>(){
new Product {ID=1, FoodName ="chicken"},
new Product{ID=2, FoodName="egg"},
new Product{ID=1, FoodName="fish"}
};
var output = list.Where(s => s.ID == 1).Select(s=>s.FoodName).ToArray(); //result will be array of items.
Use string.Join to get output results "chicken,fish"
string.Join(",", output); //result will be chicken,fish
Since you can't provide us your code. I'm assuming you have a list of food [List<Food>] and you have an id and foodName inside of it. What you can do is this:
IEnumerable<string> selectedFood = foods.FindAll(food => food.id == 1).Select(food => food.foodName);
string result = string.Join(", ", selectedFood);
You can now use the result as your label's value.
Hope this helps!
If you need to concatenate all values:
String chickenVar = "Chicken";
String fishVar = "Fish";
String result = chickenVar +", " + fishVar;

DataReader value row by row

I know I can use while (dr.Read()) {...} but that loops every field on my table, I want to retrieve all the values from the first row, and then second... and so on.
Let's say I have a table like this:
ID--------------Value1--------------Value2------------------Value3
1 hello hello2 hello3
2 hi1 hi2 hi3
3 hey1 hey2
I want output as : hello hi1 hey1
From the comment below:
flag
SqlCeCommand cmd = new SqlCeCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Select * From DataLogger1 where Time BETWEEN '"+DateTimePicker.Value+"' AND '"+DateTimePicker1.Value+"' ";
cmd.Connection = conn1;
conn1.Open();
SqlCeDataReader rd= cmd.ExecuteReader();
while(rd.Read())
{
Globals.Tags.Xarrray[0].Value=rd[2].ToString();
Globals.Tags.Xarrray[1].Value=rd[2].ToString();
Globals.Tags.Xarrray[2].Value=rd[2].ToString();
Globals.Tags.Xarrray[3].Value=rd[2].ToString();
Globals.Tags.Xarrray[4].Value=rd[2].ToString();
Well, so you want first three records which all are from the 2nd column:
i want to store Value 1 column value as: textbox1.text=hello
textboxw.text=hi1 textbox3.text=hey1
Something like this:
using (var reader = myCommand.ExecuteReader()) {
if (reader.Read())
textbox1.Text = Convert.ToString(reader.GetValue(1)); // hello
if (reader.Read())
textbox2.Text = Convert.ToString(reader.GetValue(1)); // hi1
if (reader.Read())
textbox3.Text = Convert.ToString(reader.GetValue(1)); // hey1
}
Just three conseq. reads, no while loop
To get:
hello hi1 hey1
just ignore the other columns (or don't select them if you don't need them) and only output Value1. Don't put a line break in if you really want to keep it on the same line.
You can read your input line by line, fill some structures with the data and then extract what you want using linq.
You can define a structure like:
class Data
{
public string Id;
public string Value1;
public string Value2;
public string Value3;
}
Then read all your data.
var allData = new List<Data>();
while(dr.Read())
{
var d = new Data { Id = dr.GetString(0), Value1 = dr.GetString(1), Value2 = dr.GetString(2), Value = dr.GetString(3) };
allData.Add(d);
}
var result = allData.Select(x => x.Value1);
Result is an IEnumerable< string > which contains your values. You can then do whatever you want with the values.

Auto increment ID with string

I'm using Access 2000-2003 database for C#. I like to have my TypeID to have a unique id as CHHTP001, CHHTP002 and etc, and display the id in TextBox before inserting new value in the database.
public void generateRateID()
{
string id;
con.Open();
string rate = "SELECT MAX(TypeID) FROM tblRoomTypeRate";
cmd = new OleDbCommand(rate, con);
OleDbDataReader dr = cmd.ExecuteReader();
if (dr.Read())
{
id = dr[0].ToString();
id = id + 1;
txtRateID.Text = "CHHRT" + id.PadLeft(3, '0');
}
con.Close();
}
The display in TextBox is CHHTP001 at first, then CHHTPCHHTP0011. How can I make the CHHTPCHHTP0011 to CHHTP002 and so on?
Use Regex. This should works as you want:
id = dr[0].ToString();//CHHTP001 Or CHHTP111
id = Regex.Replace(id, "\\d+", m => (int.Parse(m.Value) + 1).ToString(new string('0', m.Value.Length)));
txtRateID.Text = id;//CHHTP002 Or CHHTP112
Here is some general guidelines.
You should create an int variable that records what should be the number. That way you can manage your id easily. When you want to increment your id, just increment the variable.
When you want to convert the id to a string, first call ToString and PadLeft. Next you just concatenate the string to "CHHTP"! Isn't that easy?
Try this:
{
string id;
string literal; // "CHHRT" or whatever;
int sequence;
con.Open();
string rate = "SELECT MAX(TypeID) FROM tblRoomTypeRate";
cmd = new OleDbCommand(rate, con);
OleDbDataReader dr = cmd.ExecuteReader();
if (dr.Read())
{
id = dr[0].ToString();
sequence = int.Parse( new string( id.Where( char.IsDigit ).ToArray() ) );
literal = new string( id.Where( char.IsLetter ).ToArray() );
id = literal + ( sequence + 1 ).ToString( "000" );
txtRateID.Text = id;
}
con.Close();
}

Pass Array Parameter in SqlCommand

I am trying to pass array parameter to SQL commnd in C# like below, but it does not work. Does anyone meet it before?
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
sqlComm.Parameters.Add("#Age", SqlDbType.NVarChar);
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
sb.Append(item.Text + ",");
}
}
sqlComm.Parameters["#Age"].Value = sb.ToString().TrimEnd(',');
You will need to add the values in the array one at a time.
var parameters = new string[items.Length];
var cmd = new SqlCommand();
for (int i = 0; i < items.Length; i++)
{
parameters[i] = string.Format("#Age{0}", i);
cmd.Parameters.AddWithValue(parameters[i], items[i]);
}
cmd.CommandText = string.Format("SELECT * from TableA WHERE Age IN ({0})", string.Join(", ", parameters));
cmd.Connection = new SqlConnection(connStr);
UPDATE: Here is an extended and reusable solution that uses Adam's answer along with his suggested edit. I improved it a bit and made it an extension method to make it even easier to call.
public static class SqlCommandExt
{
/// <summary>
/// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
/// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN ({paramNameRoot}))
/// </summary>
/// <param name="cmd">The SqlCommand object to add parameters to.</param>
/// <param name="paramNameRoot">What the parameter should be named followed by a unique value for each value. This value surrounded by {} in the CommandText will be replaced.</param>
/// <param name="values">The array of strings that need to be added as parameters.</param>
/// <param name="dbType">One of the System.Data.SqlDbType values. If null, determines type based on T.</param>
/// <param name="size">The maximum size, in bytes, of the data within the column. The default value is inferred from the parameter value.</param>
public static SqlParameter[] AddArrayParameters<T>(this SqlCommand cmd, string paramNameRoot, IEnumerable<T> values, SqlDbType? dbType = null, int? size = null)
{
/* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually.
* Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
* IN statement in the CommandText.
*/
var parameters = new List<SqlParameter>();
var parameterNames = new List<string>();
var paramNbr = 1;
foreach (var value in values)
{
var paramName = string.Format("#{0}{1}", paramNameRoot, paramNbr++);
parameterNames.Add(paramName);
SqlParameter p = new SqlParameter(paramName, value);
if (dbType.HasValue)
p.SqlDbType = dbType.Value;
if (size.HasValue)
p.Size = size.Value;
cmd.Parameters.Add(p);
parameters.Add(p);
}
cmd.CommandText = cmd.CommandText.Replace("{" + paramNameRoot + "}", string.Join(",", parameterNames));
return parameters.ToArray();
}
}
It is called like this...
var cmd = new SqlCommand("SELECT * FROM TableA WHERE Age IN ({Age})");
cmd.AddArrayParameters("Age", new int[] { 1, 2, 3 });
Notice the "{Age}" in the sql statement is the same as the parameter name we are sending to AddArrayParameters. AddArrayParameters will replace the value with the correct parameters.
I wanted to expand on the answer that Brian contributed to make this easily usable in other places.
/// <summary>
/// This will add an array of parameters to a SqlCommand. This is used for an IN statement.
/// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN (returnValue))
/// </summary>
/// <param name="sqlCommand">The SqlCommand object to add parameters to.</param>
/// <param name="array">The array of strings that need to be added as parameters.</param>
/// <param name="paramName">What the parameter should be named.</param>
protected string AddArrayParameters(SqlCommand sqlCommand, string[] array, string paramName)
{
/* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually.
* Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the
* IN statement in the CommandText.
*/
var parameters = new string[array.Length];
for (int i = 0; i < array.Length; i++)
{
parameters[i] = string.Format("#{0}{1}", paramName, i);
sqlCommand.Parameters.AddWithValue(parameters[i], array[i]);
}
return string.Join(", ", parameters);
}
You can use this new function as follows:
SqlCommand cmd = new SqlCommand();
string ageParameters = AddArrayParameters(cmd, agesArray, "Age");
sql = string.Format("SELECT * FROM TableA WHERE Age IN ({0})", ageParameters);
cmd.CommandText = sql;
Edit:
Here is a generic variation that works with an array of values of any type and is usable as an extension method:
public static class Extensions
{
public static void AddArrayParameters<T>(this SqlCommand cmd, string name, IEnumerable<T> values)
{
name = name.StartsWith("#") ? name : "#" + name;
var names = string.Join(", ", values.Select((value, i) => {
var paramName = name + i;
cmd.Parameters.AddWithValue(paramName, value);
return paramName;
}));
cmd.CommandText = cmd.CommandText.Replace(name, names);
}
}
You can then use this extension method as follows:
var ageList = new List<int> { 1, 3, 5, 7, 9, 11 };
var cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM MyTable WHERE Age IN (#Age)";
cmd.AddArrayParameters("Age", ageList);
Make sure you set the CommandText before calling AddArrayParameters.
Also make sure your parameter name won't partially match anything else in your statement (i.e. #AgeOfChild)
If you can use a tool like "dapper", this can be simply:
int[] ages = { 20, 21, 22 }; // could be any common list-like type
var rows = connection.Query<YourType>("SELECT * from TableA WHERE Age IN #ages",
new { ages }).ToList();
Dapper will handle unwrapping this to individual parameters for you.
If you are using MS SQL Server 2008 and above you can use table-valued parameters like described here
http://www.sommarskog.se/arrays-in-sql-2008.html.
1. Create a table type for each parameter type you will be using
The following command creates a table type for integers:
create type int32_id_list as table (id int not null primary key)
2. Implement helper methods
public static SqlCommand AddParameter<T>(this SqlCommand command, string name, IEnumerable<T> ids)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.TypeName = typeof(T).Name.ToLowerInvariant() + "_id_list";
parameter.SqlDbType = SqlDbType.Structured;
parameter.Direction = ParameterDirection.Input;
parameter.Value = CreateIdList(ids);
command.Parameters.Add(parameter);
return command;
}
private static DataTable CreateIdList<T>(IEnumerable<T> ids)
{
var table = new DataTable();
table.Columns.Add("id", typeof (T));
foreach (var id in ids)
{
table.Rows.Add(id);
}
return table;
}
3. Use it like this
cmd.CommandText = "select * from TableA where Age in (select id from #age)";
cmd.AddParameter("#age", new [] {1,2,3,4,5});
Since there is a method on
SqlCommand.Parameters.AddWithValue(parameterName, value)
it might be more convenient to create a method accepting a parameter (name) to replace and a list of values. It is not on the Parameters level (like AddWithValue) but on command itself so it's better to call it AddParametersWithValues and not just AddWithValues:
query:
SELECT * from TableA WHERE Age IN (#age)
usage:
sqlCommand.AddParametersWithValues("#age", 1, 2, 3);
the extension method:
public static class SqlCommandExtensions
{
public static void AddParametersWithValues<T>(this SqlCommand cmd, string parameterName, params T[] values)
{
var parameterNames = new List<string>();
for(int i = 0; i < values.Count(); i++)
{
var paramName = #"#param" + i;
cmd.Parameters.AddWithValue(paramName, values.ElementAt(i));
parameterNames.Add(paramName);
}
cmd.CommandText = cmd.CommandText.Replace(parameterName, string.Join(",", parameterNames));
}
}
I want to propose another way, how to solve limitation with IN operator.
For example we have following query
select *
from Users U
WHERE U.ID in (#ids)
We want to pass several IDs to filter users. Unfortunately it is not possible to do with C# in easy way. But I have fount workaround for this by using "string_split" function. We need to rewrite a bit our query to following.
declare #ids nvarchar(max) = '1,2,3'
SELECT *
FROM Users as U
CROSS APPLY string_split(#ids, ',') as UIDS
WHERE U.ID = UIDS.value
Now we can easily pass one parameter enumeration of values separated by comma.
Passing an array of items as a collapsed parameter to the WHERE..IN clause will fail since query will take form of WHERE Age IN ("11, 13, 14, 16").
But you can pass your parameter as an array serialized to XML or JSON:
Using nodes() method:
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
sb.Append("<age>" + item.Text + "</age>"); // actually it's xml-ish
sqlComm.CommandText = #"SELECT * from TableA WHERE Age IN (
SELECT Tab.col.value('.', 'int') as Age from #Ages.nodes('/age') as Tab(col))";
sqlComm.Parameters.Add("#Ages", SqlDbType.NVarChar);
sqlComm.Parameters["#Ages"].Value = sb.ToString();
Using OPENXML method:
using System.Xml.Linq;
...
XElement xml = new XElement("Ages");
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
xml.Add(new XElement("age", item.Text);
sqlComm.CommandText = #"DECLARE #idoc int;
EXEC sp_xml_preparedocument #idoc OUTPUT, #Ages;
SELECT * from TableA WHERE Age IN (
SELECT Age from OPENXML(#idoc, '/Ages/age') with (Age int 'text()')
EXEC sp_xml_removedocument #idoc";
sqlComm.Parameters.Add("#Ages", SqlDbType.Xml);
sqlComm.Parameters["#Ages"].Value = xml.ToString();
That's a bit more on the SQL side and you need a proper XML (with root).
Using OPENJSON method (SQL Server 2016+):
using Newtonsoft.Json;
...
List<string> ages = new List<string>();
foreach (ListItem item in ddlAge.Items)
if (item.Selected)
ages.Add(item.Text);
sqlComm.CommandText = #"SELECT * from TableA WHERE Age IN (
select value from OPENJSON(#Ages))";
sqlComm.Parameters.Add("#Ages", SqlDbType.NVarChar);
sqlComm.Parameters["#Ages"].Value = JsonConvert.SerializeObject(ages);
Note that for the last method you also need to have Compatibility Level at 130+.
Just changing DbType might be enough:
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand
{
Connection = sqlCon,
CommandType = CommandType.Text,
CommandText = sqlCommand,
CommandTimeout = 300
};
var itens = string.Join(',', ddlAge.Items);
sqlComm.Parameters.Add(
new SqlParameter("#Age", itens)
{
DbType = DbType.String
});
Use .AddWithValue(), So:
sqlComm.Parameters.AddWithValue("#Age", sb.ToString().TrimEnd(','));
Alternatively, you could use this:
sqlComm.Parameters.Add(
new SqlParameter("#Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar }
);
Your total code sample will look at follows then:
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
sb.Append(item.Text + ",");
}
}
sqlComm.Parameters.AddWithValue("#Age", sb.ToString().TrimEnd(','));
// OR
// sqlComm.Parameters.Add(new SqlParameter("#Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar });
Overview: Use the DbType to set the parameter type.
var parameter = new SqlParameter();
parameter.ParameterName = "#UserID";
parameter.DbType = DbType.Int32;
parameter.Value = userID.ToString();
var command = conn.CreateCommand()
command.Parameters.Add(parameter);
var reader = await command.ExecuteReaderAsync()
try it like this
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
string sqlCommand = "SELECT * from TableA WHERE Age IN (#Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
sqlComm.Parameters.Add("#Age", SqlDbType.NVarChar);
sb.Append(item.Text + ",");
sqlComm.Parameters["#Age"].Value = sb.ToString().TrimEnd(',');
}
}

Categories

Resources