Add string array to SQL query - c#

I have a string array which consists of identifiers. I want to get some values from SQL using these identifiers . Is there a way of adding them with a string value to SqlCommand parameters?
I want to create a query like:
select CaseList from MasterReportData where Id = 1 OR Id = 2 OR Id = 3
This is my C# code:
public static List<string> GetCaseList(string[] masterIdList)
{
try
{
string query = " select CaseList from MasterReportData where #masterId";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
cmd.Parameters.AddWithValue("masterId", ***);
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(reader[0].ToString());
}
}
conn.Close();
}
catch (Exception e)
{
var err= 0;
}
return list;
}

There are many different ways you can go about doing this but I prefer to create a temp table of possible values. That way you can do something like
select CaseList from MasterReportData where Id IN(select Id from tempTable)

The best approach (with sql optimization) would be:
Create your Type:
CREATE TYPE dbo.IntTTV AS TABLE
( Id int )
Your Ids:
var ids = new List<int>
{
1,
2,
3,
}
Create a schema:
var tableSchema = new List<SqlMetaData>(1)
{
new SqlMetaData("Id", SqlDbType.Int) // I think it's Int
}.ToArray();
Create the table in C#
var table = ids
.Select(i =>
{
var row = new SqlDataRecord(tableSchema);
row.SetInt32(0, i);
return row;
})
.ToList();
Create the SQL Parameter
var parameter = new SqlParameter();
parameter.SqlDbType = SqlDbType.Structured;
parameter.ParameterName = "#Ids";
parameter.Value = table;
parameter.TypeName = "dbo.IntTTV";
var parameters = new SqlParameter[1]
{
parameter
};
Slightly change your query (this is just an example:)
string query = "select mrd.CaseList from MasterReportData mrd"
+ " inner join #ids i on mrd.Id = i.id";

public static List<string> GetCaseList(string[] masterIdList)
{
List<string> list = new List<string>();
try
{
string query = "select CaseList from MasterReportData where ";
using (SqlConnection conn = new SqlConnection(connString))
{
int i = 0;
SqlCommand cmd = new SqlCommand(query, conn);
for(i = 0; i < masterIdList.Length; i++)
{
var parm = "#ID" + i;
cmd.Parameters.Add(new SqlParameter(parm, masterIdList[i]));
query += (i > 0 ? " OR " : "") + " Id = " + parm;
}
cmd.CommandText = query;
//cmd.Parameters.AddWithValue("masterId", ***);
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(reader[0].ToString());
}
}
}
}
catch (Exception e)
{
e.ToString();
}
return list;
}

Related

C# SQL Server like command not working with parameters

I tried the solution presented in many topics on the internet, but none of them work. If I don't use parameter method like is working. How can fix that?
Here is my C# code:
string kosul = textbox1.Text ;
kitapAra("kitapAdi", kosul);
public void kitapAra(string by,string kosul,int like = 0)
{
string sql = "select * from kitaplar where kitapDurum = 0 and #byP like #kosulP order by kitapId ";
DataTable dt = DbOperations.cmd_SelectQuery(sql, new List<string> { "#byP", "#kosulP" }, new List<object> { by,kosul });
refreshDataGrid(dt);
}
Here is my SQL connect method:
public static DataTable cmd_SelectQuery(string srCommandText, List<string> lstParameterNames, IList<object> lstParameters, bool blsetCommit = false, int like = 0)
{
if (blsetCommit == true)
{
srCommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED " + Environment.NewLine + " " + srCommandText;
}
DataTable dsCmdPara = new DataTable();
try
{
using (SqlConnection connection = new SqlConnection(DbOperations.srConnectionString))
{
using (SqlCommand cmd = new SqlCommand(srCommandText, connection))
{
cmd.CommandType = CommandType.Text;
for (int i = 0; i < lstParameterNames.Count; i++)
{
cmd.Parameters.AddWithValue(lstParameterNames[i], lstParameters[i].ToString());
}
using (SqlDataAdapter sqlDa = new SqlDataAdapter(cmd))
{
sqlDa.Fill(dsCmdPara);
return dsCmdPara;
}
}
}
}
return dsCmdPara;
}
I tried them:
DataTable dt = DbOperations.cmd_SelectQuery(sql, new List<string> { "#byP", "#kosulP" }, new List<object> { by,"%" + kosul +"%"});
or:
cmd.Parameters.AddWithValue(lstParameterNames[i],"%" + lstParameters[i].ToString()+"%");
or:
"select * from kitaplar where kitapDurum = 0 and #byP like %#kosulP% order by kitapId ";
or:
"select * from kitaplar where kitapDurum = 0 and #byP like "%" + #kosulP + "%" order by kitapId ";

invalid attempt to call read when READER IS CLOSED .. error repeat at every line

I am getting this error while i am trying to retrieve data from table.,
I am getting error at every line in while(r1.read()) loop., if i remove that line then i am getting same error in next line, it continues till last line of loop..
Please help.,
void otherdata()
{
if (conn.State == ConnectionState.Closed) conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "select customer.cust_name,customer.cust_id,purchase_details.purchase_id,purchase_details.pdate,purchase_details.subtotal,purchase_details.total,purchase_details.supp_bill_id from Purchase_details inner join customer on purchase_details.supplier_id=customer.cust_id where id =" + pd;
SqlDataReader r1 = cmd.ExecuteReader();
while (r1.Read())
{
cbsupplier.Text = r1["cust_name"].ToString();
txtdate.Value = Convert.ToDateTime(r1["pdate"]);
txtsubtotal.Text = r1["subtotal"].ToString();
custid.Text = r1["cust_id"].ToString();
MessageBox.Show("pochalo");
}
r1.Close();
conn.Close();
if (conn.State == ConnectionState.Closed) conn.Open();
cmd.CommandText = "select * from purchase where purchase_id =" + pd;
r1 = cmd.ExecuteReader();
while (r1.Read())
{
txtadjust.Text = r1["adjustment"].ToString();
cbtaxrate.Text = r["taxrate"].ToString();
txttax.Text = r["tax"].ToString();
}
conn.Close();
r1.Close();
}
for your exception, i'm guessing is throwing this exception at this line :
cbtaxrate.Text = r["taxrate"].ToString();
txttax.Text = r["tax"].ToString();
because r["taxrate"] is not the same reader as r1["adjustment"]. So, i'm guessing you opened a reader with a name r in the same scope, then closed it, and opened a new reader r1 and did the above code. the issue here is not about closing the reader connection, it's about following the best practices in handling instances and naming conversion. if you chose a readable naming for your variables, it will be easer for you to read and follow the code.
You need also to use using clause on disposable objects, for a better disposal handling.
Here is a helper method that would saves you the hassle:
public static class SqlConnectionHelper
{
public static IEnumerable<KeyValuePair<string , object>> GetQueryValues(this SqlConnection connection , string sql , SqlParameter[] parameters , string[] fields)
{
if(connection == null)
{
throw new ArgumentNullException(nameof(connection));
}
var mappedFields = new Dictionary<int, string>();
using(var command = new SqlCommand(sql , connection))
{
if(parameters?.Length > 0)
{
command.Parameters.AddRange(parameters);
}
if(connection.State == ConnectionState.Closed)
connection.Open();
using(var reader = command.ExecuteReader())
{
if(reader.HasRows)
yield break;
if(fields?.Length > 0)
{
for(int x = 0; x < reader.FieldCount; x++)
{
var columnName = reader.GetName(x);
if(Array.Exists(fields , i => i.Equals(columnName , StringComparison.OrdinalIgnoreCase)))
{
mappedFields.Add(x , columnName);
}
}
while(reader.Read())
{
foreach(var field in mappedFields)
{
yield return new KeyValuePair<string , object>(field.Value , reader.GetValue(field.Key));
}
}
}
else
{
while(reader.Read())
{
for(int x = 0; x < reader.FieldCount; x++)
{
yield return new KeyValuePair<string , object>(reader.GetName(x) , reader.GetValue(x));
}
}
}
}
}
}
public static Dictionary<string , object> GetQueryUniqueValues(this SqlConnection connection , string sql , SqlParameter[] parameters , string[] fields)
{
return GetQueryValues(connection , sql , parameters , fields)
.GroupBy(x => x.Key)
.Select(x => new
{
Name = x.Key ,
Value = x.FirstOrDefault(s => s.Key == x.Key).Value
})
.ToDictionary(x => x.Name , x => x.Value);
}
}
with the reader helper, you can provide fields to get selected columns values, or you can just pass null to it and it will read all columns that have been provided under the SELECT statement.
reusing this to your current code would be like :
using(var connection = new SqlConnection(connectionString))
{
const string purchaseDetailsSql = "SELECT c.cust_name, c.cust_id, p.purchase_id, p.pdate, p.subtotal, p.total, p.supp_bill_id FROM Purchase_details p INNER JOIN customer c ON p.supplier_id = c.cust_id WHERE id = #id ;";
const string purchasesSql = "SELECT adjustment, taxrate, tax FROM purchase WHERE purchase_id = #id ;";
var parameters = new[]
{
new SqlParameter("#id", pd)
{
SqlDbType = SqlDbType.Int
}
};
var fields = new[] { "cust_name", "pdate" , "subtotal", "cust_id" };
// here would select the columns from the provided fields array.
var purchaseDetails = connection.GetQueryUniqueValues(purchaseDetailsSql , parameters, new[] { "cust_name", "pdate" , "subtotal", "cust_id" });
// here would select the columns from the query.
var purchases = connection.GetQueryUniqueValues(purchasesSql, parameters, null);
cbsupplier.Text = purchaseDetails["cust_name"]?.ToString();
txtdate.Value = Convert.ToDateTime(purchaseDetails["pdate"]);
txtsubtotal.Text = purchaseDetails["subtotal"]?.ToString();
custid.Text = purchaseDetails["cust_id"]?.ToString();
MessageBox.Show("pochalo");
txtadjust.Text = purchases["adjustment"]?.ToString();
cbtaxrate.Text = purchases["taxrate"]?.ToString();
txttax.Text = purchases["tax"]?.ToString();
}

C# SqlDataReader keeps reading

I have a method for getting data out of my SQL Server database. I'm using a reader to get all the data. The problem is that the reader keeps reading and the application doesn't pop up. It is like an infinite loop or something.
public static List<Reservering> GetReserverings()
{
Reservering res = new Reservering();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
}
reader.Close();
}
return GetReserverings();
}
Does anyone know how to fix this problem?
You've got an infinite recursion by calling the method itself at the end:
return GetReserverings();
You could've discovered this by setting a breakpoint in your method and stepping through your code.
You want to return a list of reservations instead:
var result = new List<Reservering>();
// your code...
return result;
And within your while() loop, you'd want to instantiate a new Reservering each iteration, and Add it to the result list.
You should create an instance of Reservering for each record read and store these instances in the List<Reservering>:
public static List<Reservering> GetReserverings() {
List<Reservering> result = new List<Reservering>();
using (var conn = new SqlConnection(ConnectionString)) {
conn.Open();
const string query =
#"select b.boekingid,
k.naam,
bk.incheckdatum,
bk.uitcheckdatum,
b.hotelid,
b.aantal_gasten
from boeking b join
klant k on k.klantid = b.boekingid join
boekingkamer bk on b.boekingid = bk.boekingid
where bk.incheckdatum is not null
and bk.uitcheckdatum is not null";
using (SqlCommand selectReserveringen = new SqlCommand(query, conn)) {
using (SqlDataReader reader = selectReserveringen.ExecuteReader()) {
while (reader.Read()) {
Reservering res = new Reservering();
result.Add(res);
res.Id = Convert.ToInt32(reader["boekingid"]);
res.Naam = Convert.ToString(reader["naam"]);
res.Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]);
res.Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]);
res.Hotel = Convert.ToInt32(reader["hotelid"]);
res.Aantal_personen = Convert.ToInt32(reader["aantal_gasten"]);
}
}
}
}
return result;
}
Edit: You can try to simplify while loop with a help of object initializing:
...
while (reader.Read()) {
result.Add(new Reservering() {
Id = Convert.ToInt32(reader["boekingid"]),
Naam = Convert.ToString(reader["naam"]),
Incheck_datum = Convert.ToDateTime(reader["incheckdatum"]),
Uitcheck_datum = Convert.ToDateTime(reader["uitcheckdatum"]),
Hotel = Convert.ToInt32(reader["hotelid"]),
Aantal_personen = Convert.ToInt32(reader["aantal_gasten"])
});
}
public static List<Reservering> GetReserverings()
{
List<Reserving> reservings = new List<Reservings>();
using (var conn = new SqlConnection(ConnectionString))
{
conn.Open();
const string query = "select b.boekingid, k.naam, bk.incheckdatum, bk.uitcheckdatum, b.hotelid, b.aantal_gasten from boeking b join klant k on k.klantid = b.boekingid join boekingkamer bk on b.boekingid = bk.boekingid where bk.incheckdatum is not null and bk.uitcheckdatum is not null";
SqlCommand selectReserveringen = new SqlCommand(query, conn);
SqlDataReader reader = selectReserveringen.ExecuteReader();
while (reader.Read())
{
Reservering res = new Reservering();
res.Id = (int)reader["boekingid"];
res.Naam = (string)reader["naam"];
res.Incheck_datum = (DateTime)reader["incheckdatum"];
res.Uitcheck_datum = (DateTime)reader["uitcheckdatum"];
res.Hotel = (int)reader["hotelid"];
res.Aantal_personen = (int)reader["aantal_gasten"];
reservings.add(res);
}
reader.Close();
}
return reservings;
}

How to query a C# list against a database table

My code isn't returning any rows from a test database table when I pass a string version of a list, but it does return rows if I pass the list members in directly.
When I use a message box to show the string joinedSerialsList, it appears to be formatted properly.
// Create comma delimited list of serials:
int currentSerial = beginning;
List<string> serialsList = new List<string>();
for (int i = 0; i < count; i++)
{
serialsList.Add(currentSerial.ToString());
currentSerial++;
}
string joinedSerialsList = string.Format("({0})", string.Join(", ", serialsList));
OleDbConnection connection = BadgeDatabaseDB.GetConnection();
string checkStatement
= "SELECT SerialNumber, OrderNumber "
+ "FROM SerialNumbersMFG "
+ "WHERE SerialNumber IN (#List)";
OleDbCommand command = new OleDbCommand(checkStatement, connection);
command.Parameters.AddWithValue("#List", joinedSerialsList);
string duplicateSerials = "";
try
{
connection.Open();
OleDbDataReader dataReader = command.ExecuteReader();
if (dataReader.Read())
{
duplicateSerials += dataReader["OrderNumber"].ToString() + "\n";
}
}
catch (OleDbException ex)
{
throw ex;
}
finally
{
connection.Close();
}
return duplicateSerials;
I rewrited your sample, this work:
private IEnumerable<string> getData()
{
// Create comma delimited list of serials:
int currentSerial = 4452; // your constant
var serialsList = new List<int>();
var count = 100;
for (int i = 0; i < count; i++)
serialsList.Add(currentSerial++);
var connString = getConnectionString();
var results = new List<string>();
string sqlSelect = $"SELECT SerialNumber, OrderNumber FROM SerialNumbersMFG WHERE SerialNumber IN ({string.Join(",", serialsList)})";
using (var connection = new SqlConnection(connString)) // BadgeDatabaseDB.GetConnection();
{
using (var command = new SqlCommand(sqlSelect, connection))
{
connection.Open();
var dataReader = command.ExecuteReader();
while (dataReader.Read())
results.Add(dataReader["OrderNumber"].ToString());
}
}
return results;
}

C# search method

I'm having trouble coming up with the following search method:
public override List<Team> Search(Dictionary<string, string> prms, int pageSize, int page, out int results)
{
var tresults = new List<Team>();
string temp1 = "";
string temp2 = "";
using (SqlConnection conn = DB.GetSqlConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"Search";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
foreach (KeyValuePair<string, string> pair in prms)
{
temp1 = pair.Key;
temp2 = pair.Value;
}
if (temp1 == "TeamName")
{
SqlParameter p1 = new SqlParameter("TeamName", System.Data.SqlDbType.VarChar);
p1.Value = temp2;
cmd.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter("CityName", System.Data.SqlDbType.VarChar);
p2.Value = null;
cmd.Parameters.Add(p2);
}
else if (temp1 == "CityName")
{
SqlParameter p1 = new SqlParameter("TeamName", System.Data.SqlDbType.VarChar);
p1.Value = null;
cmd.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter("CityName", System.Data.SqlDbType.VarChar);
p2.Value = temp2;
cmd.Parameters.Add(p2);
}
SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
}
//results = 1 + 1;
throw new NotImplementedException("Must be implemented by class. ");
}
What I'm trying to do is basically what this test is doing:
[TestMethod]
public void SearchForTeam()
{
var dic = new Dictionary<string, string>();
int total = 0;
dic.Add("TeamName", "Patriots");
var nd = new TeamRepository();
var teams = nd.Search(dic, 100, 1, out total);
Assert.IsTrue(teams.Find(p => p.TeamName == "Patriots") != null);
}
What I'm trying to do is have my method search by either Team Name (SQL column "TeamName", value "Patriots") or by City Name (SQL column "CityName" value "Chicago", etc. I think my issues mainly are that I'm not entirely sure if I'm understanding how the dictionary works.
Also, I'm not sure how the value I'm returning should work because I am both returning an int (from the out parameter) and type List. This is all pretty new to me, so its the basics that I don't quite understand I suppose.
How about this?
public override List<Team> Search(Dictionary<string, string> prms, int pageSize, int page)
{
var tresults = new List<Team>();
using (SqlConnection conn = DB.GetSqlConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"Search";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
foreach (KeyValuePair<string, string> pair in prms)
cmd.Parameters.Add(new SqlParameter(pair.Key, System.Data.SqlDbType.VarChar) { Value = pair.Value });
SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
// I assume you'll use pageSize and page here?
}
}
return tresults; // I assume this is what you want to return.
}
If you don't want to use a specific column for your search, then there's no need to create a SqlParameter for that column and sets its Value to null -- just don't use that column!
Also, there's no need to have out int results. If you're returning the list of teams, then the invoker can just get the team count from the list (teams.Count). (If you are doing something else with results, then by all means ignore this paragraph.)
it's kind of hard to see what you are getting at here, I am unsure that you need a dictionary at all (would you pass in multiple records?)
personally I would do the below, the assumption is the stored procedure could handle the possibility of both parameters being populated if both are passed in completed.
public override List<Team> Search(string teamName,string cityName, int pageSize, int page)
{
var tresults = new List<Team>();
using (SqlConnection conn = DB.GetSqlConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"Search";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter p1 = new SqlParameter("TeamName", System.Data.SqlDbType.VarChar);
p1.Value = teamName;
cmd.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter("CityName", System.Data.SqlDbType.VarChar);
p2.Value = cityName;
cmd.Parameters.Add(p2);
SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
while(reader.Read())
{
tresults.Add(BuildTeamFromReader(reader));
}
}
}
return tresults;
}
private Team BuildTeamFromReader(SqlDataReader reader)
{
var team = new Team();
team.TeamName = reader["TeamName"];//or whatever your column name is for team name
//ToDo other mappings
return team;
}

Categories

Resources