Converting a DataTable to my List<T> - c#

cmd.CommandText = sql;
cmd.ExecuteNonQuery();
SqliteDataReader rdr = cmd.ExecuteReader();
dt.Load(rdr);
return dt;
DataTable tb = new DataTable(); to the List<Details> objList = new List<Details>.
foreach (var obj in dt)
{
var item = new Details();
item.ID = obj.ID;
item.Dosage = obj.Dosage;
item.Drug = obj.Drug;
item.Patient = obj.Patient;
item.Date = obj.Date;
results.Add(item);
}
the idea is here but there is an error in dt in forceach(var obj in dt)

You have to
Execute reader
Read the coursor
Create your class instances
Collect these instances into a List<T>
Something like this
// List which will be used to collect records from the query
List<MyClass> list = new List<MyClass>();
...
cmd.CommandText = sql;
//DONE: execute reader; wrap IDisposable into using
using (SqliteDataReader rdr = cmd.ExecuteReader()) {
//DONE: read each cursor's record
while (rdr.Read()) {
//DONE: Turn each record into MyClass instance
MyClass item = new MyClass();
//TODO: Assign properties
item.Property1 = Convert.ToString(rdr[0]);
item.Property2 = Convert.ToInt32(rdr[1]);
...
//DONE: Finally, collect all the intances created into the list
list.Add(item);
}
}
...

Related

set result of method to variable i can return in text field form c#

I have a method that returns a string from a database based on what i pass to it. The method works but i need to display the result in a textbox on a form. But when i run it the result i keep getting is "System.Collections.Generic.List`1[System.String]". I cant work this out. I try looping through the interest variable with for loop but this doesn't work either.
var interest = GetValue2(tvp, connectionString); -- this is my method that returns a string
textBox2.Text = string.Join(", ", interest.ToString());
this returns "System.Collections.Generic.List`1[System.String]" to textbox2. Any ideas what im missing here? thanks
PART 2:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((String)rdr["id"]);
}
Console.ReadLine();
}
}
return items;
}
Could you share the GetValue2 method? Probably you are calling ToString() on List in that method and it that case the GetValue2 method returns
"System.Collections.Generic.List1[System.String]"
Example:
var test = new List<string>{"some text"};
test.ToString(); // returns System.Collections.Generic.List`1[System.String]
Update:
Because the method GetValue2 returns List<string> avoid calling .ToString() on interest
var interest = GetValue2(tvp, connectionString); // returns List<string>
textBox2.Text = string.Join(", ", interest);
There's also the issue of the List<string> items inside the GetValue2() not being populated.
Here's the corrected method:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
items.Add((String)rdr["id"]);
}
}
}
return items;
}

SQL query result assertion

I have put together the following method:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
}
return valuesList;
}
I'd like to be able to run an assertion like this one:
var a = DbQueryToArry();
Assert.IsTrue(a.Contains("some value"));
Given reader [0]
valuesList.Add(Convert.ToString(reader[0]));
I only get the first column (CLIENTINFO) into the array and not the second (ACCOUNT_Purpose). How should I modify the code to get both ?
In addition, the returned values could be either a String or Int so would my current code version should be handling both ?
Thanks in advance.
If we switch from obsolete ArrayList to something like IEnumerable<T>:
public static IEnumerable<IDataRecord> DbQueryToArray(string sql) {
if (null == sql)
throw new ArgumentNullException(nameof(sql));
//TODO: do not hardcode connetcion string but read it (say, from Settings)
string SqlCString = "connString";
//DONE: Wrap IDisposable into using
using (SqlConnection connection = new SqlConnection(SqlCString)) {
connection.Open();
//DONE: Wrap IDisposable into using
using (SqlCommand command = new SqlCommand(sql, connection)) {
//DONE: Wrap IDisposable into using
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
yield return reader as IDataRecord;
}
}
}
}
}
then you can use Linq in order to query the result:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT");
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["CLIENTNO"]) == "some value"));
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["ACCOUNT_Purpose"]) == "some other value"));
If you don't want execute query several times, you can materialize the results:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.ToList();
Assert.IsTrue(a.Any(record => Convert.ToString(record[0]) == "some value"));
Assert.IsTrue(a.Any(record => Convert.ToString(record[1]) == "some other value"));
Finally (see comments below), if we want to test if any field in any record has the value:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.SelectMany(line => {
// Flatten the cursor into IEnumerable<String>
string[] result = new string[line.FieldCount];
for (int i = 0; i < result.Length; ++i)
result[i] = Convert.ToString(line[i]);
return result;
});
a.Any(item => item == "some value");
It is because you only read the first value of the reader. Reader.Read() read each row one by one and Convert.ToString(reader[0])) means that you want to read the first column as string.
That's cause you are getting only the first column. You can do something like below by specifying the column name
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader["CLIENTNO"]));
valuesList.Add(Convert.ToString(reader["ACCOUNT_Purpose"]));
}
Moreover, since you are converting all the columns to string; I would suggest to use a strongly typed collection like List<string> rather then ArrayList valuesList = new ArrayList();
The other answers are good, However some concerns
Lets stop using ArrayList, and use a List<T> instead
Lets use a using statement where you can
Note : I have used a ValueTuple to return more than 1 field
Example
public static List<(string clientNo, string account)> DbQueryToArray()
{
const string SqlCString = "connString";
var valuesList = new List<(string clientNo, string account)>();
using (var connection = new SqlConnection(SqlCString))
{
connection.Open();
using (var command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection))
{
var reader = command.ExecuteReader();
while (reader.Read())
valuesList.Add(((string)reader[0],(string)reader[1]) );
}
}
return valuesList;
}
Usage
var results = DbQueryToArray();
Assert.IsTrue(results.Any(x => x.clientNo == someValue || x.account == someValue));
best practice is to check first if the reader has rows
reader.HasRows
then close the reader and the connection
your code should look like this:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
using (connection)
{
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
valuesList.Add(Convert.ToString(reader[1])); // add to valuelist
}
}
reader.Close(); // close reader
} //dispose connection
return valuesList;
}
Use DataTable and SqlDataAdapter to get query result in form of table. Something like this:
string connString = #"your connection string here";
string query = "select * from table";
DataTable dataTable = new DataTable();
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
Then you can use dataTable object to see if particular values exist.

SqlDataReader closed for unknown reason

C# noob here. I am trying to get some data from a SQL Data Base using the code below, however i get the error: System.InvalidOperationException: 'Invalid attempt to call Read when reader is closed.'
After searching for the answer online, i found nothing, maybe someone can see what is wrong?
The error happens at: while (rdr.Read())
Thank you!
internal List<string> GetInflationByYearData(string Country, int StartYear,
int EndYear)
{
string CS =
ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("SpCountryData", con)
{
CommandType = System.Data.CommandType.StoredProcedure
};
cmd.Parameters.AddWithValue("#act",
"getInflationByYearData");
cmd.Parameters.AddWithValue("#Country", Country);
cmd.Parameters.AddWithValue("#startYear", StartYear);
cmd.Parameters.AddWithValue("#endYear", EndYear);
var table = new DataTable();
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
{
table.Load(rdr);
};
}
//con.Close();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
if (table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
string label = row["Year"].ToString();
string value = row["Percentage"].ToString();
labelList.Add(label);
valueList.Add(value);
}
}
List<string> list = new List<string>();
StringBuilder sbLabels = new StringBuilder();
foreach (string lbl in labelList)
{
sbLabels.Append(lbl + ",");
}
StringBuilder sbValues = new StringBuilder();
foreach (string val in valueList)
{
sbValues.Append(val + ",");
}
list.Add(sbLabels.ToString().Substring(0, sbLabels.Length -
1));
list.Add(sbValues.ToString().Substring(0, sbValues.Length -
1));
return list;
}
}
Load consumes the reader to completion; you should either have a while (rdr.Read()) loop, or call table.Load(rdr), but: not both. Load basically does that same loop internally.
So: remove the loop - just use Load.
However! If all you want is the data as List<string>, loading it into a DataTable seems like an unnecessary step (DataTable is not lightweight) - it seems like you could do that in the reader, or perhaps use other tools (like "dapper") to remove that.
It isn't clear what data types Year and Percentage are, but as a "dapper" example...
class MyRowThing {
public int Year {get;set;}
public decimal Percentage {get;set;}
}
var rawData = con.Query<MyRowThing>("SpCountryData",
new { // params here
act = "getInflationByYearData", Country,
startYear = StartYear, endYear = EndYear
}, commandType: CommandType.StoredProcedure).AsList();
// now loop over rawData, which is a List<MyRowThing> with the data
Replacing:
var table = new DataTable();
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
{
table.Load(rdr);
};
}
//con.Close();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
if (table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
string label = row["Year"].ToString();
string value = row["Percentage"].ToString();
labelList.Add(label);
valueList.Add(value);
}
}
With:
con.Open();
List<string> labelList = new List<string>();
List<string> valueList = new List<string>();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
labelList.Add(rdr["Year"].ToString());
valueList.Add(rdr["Percentage"].ToString());
}
Will get rid of the DataTable for a more optimized code.

inserting into data sourced combobox

i am working on visual studio 2012 c# ...
i inserted values into the combox ...i took them from database...I WANT TO KNOW HOW CAN I ADD AN ITEM TO THE COMBOBOX ...ill show u the code below:
Here this function to fill the combobox with names taken from a table in database containig name and id:
List<Lookup> fillCombo(string query, string column)
{
List<Lookup> lookups = new List<Lookup>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Lookup lookupobject = new Lookup();
lookupobject.ID = Convert.ToInt32(reader["ID"]);
//if (reader["Name"] != DBNull.Value)
lookupobject.Name = reader[column].ToString();
lookups.Add(lookupobject);
}
conn.Close();
}
return lookups;
}
then i call this function as follows:
lookups = fillCombo("select id,name from LookupDetails where LOOKUPID = (select id from Lookup where Name = 'users')", "name");
comboBox2.DataSource = lookups;
comboBox2.DisplayMember = "name";
ComboxBox Items collection cannot be modified when the DataSource property is set.
You got the options of either modifying the List<Lookup> or by adding items into combox by iterating over the List<Lookup>.
Here is option to add items in combobox using foreach loop and insert item at 0 index of comboxbox:
lookups = fillCombo(#"select id,name from LookupDetails where LOOKUPID =
(select id from Lookup where Name = 'users')", "name");
foreach(var obj in lookups)
comboBox2.Items.Add(obj);
comboBox2.DisplayMember = "Name";
comboBox2.Items.Insert(0, "");
Note: In the code mentioned in the question, SqlDataReader was never closed, I have modified it by including using statement. You don't have to close SqlDataReader or SqlConnection when you write these in using block:
List<Lookup> fillCombo(string query, string column)
{
List<Lookup> lookups = new List<Lookup>();
string sConstring = ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString;
using (SqlConnection conn = new SqlConnection(sConstring))
using(SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
using(SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Lookup lookupobject = new Lookup();
lookupobject.ID = Convert.ToInt32(reader["ID"]);
//if (reader["Name"] != DBNull.Value)
lookupobject.Name = reader[column].ToString();
lookups.Add(lookupobject);
}
}
}
return lookups;
}

store the data coming from database in generic list

I am using generic list to store the data that comes by querying the databse.I uses List of classes actually for multiple rows.
But my problem is my classes have almost more than 20 properties and most of the time i uses only its 2 or 3 properties.
So I want to know that what is the best way to keep the data coming from database.
Below is my code
List<ImageGalleryCollection> tempList = new List<ImageGalleryCollection1>();
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList, connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
tempList.Add(igc);
}
data.Close();
connection.Close();
return tempList;
In ImageGalleryCollection I have more that 20 properties and above i only uses two properties.I think it is very inefficient
Can you how your base class implementation? You can create another class with the most using attributes and use an object of that class inside your class.
IEnumerable<ImageGalleryCollection> GetImageGalleryCollection()
{
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList,connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
yield return igc;
}
data.Close();
connection.Close();
}
I would like to suggest you to write a extension method for SqlDataReader and make use of the method in linq to fetch required columns from the returned rows of reader.
Extension method:
public static class DataReaderExtension
{
public static IEnumerable<Object[]> DataRecord(this System.Data.IDataReader source)
{
if (source == null)
throw new ArgumentNullException("source");
while (source.Read())
{
Object[] row = new Object[source.FieldCount];
source.GetValues(row);
yield return row;
}
}
}
using it in linq:
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Select * from tblUser"))
{
cmd.CommandType = CommandType.Text;
cmd.Connection = cn;
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var result = (from row in dr.DataRecord()
select new
{
UserId = row[0],
UserName = row[1]
}).ToList();
}
}
}
This result list has only the required properties you select and helps to reduce the consumption of memory for unwanted properties.

Categories