Suppose I have this code (pseudocode)
class SomeClass
{
class Person
{
public static string Name { get; set; }
public static int Age { get; set; }
}
List<Person> person = new List<person>;
public void SelectPerson()
{
DataTable dt = new DataTable();
SqlConnection conn = GetConnection();
conn.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT name, age FROM person", conn);
da.Fill(dt);
}
}
Can I fill the List (person) based on the result of my DataAdapter?
How should I do it? Or is there any workaround? Thanks...
Probably the best way is not to read into a datatable first:
var dr = new DataReader(....) // Fill in what is needed, can't remember offhand
while(dr.Next())
{
persons.Add(
new Person() {
Name = (string) r["Name"],
Age = (int) r["Age"]
}
);
}
Caveat: You want to close the DataReader/connection quickly, don't do lots of processing. The above code is more efficient than using a DataTable as an intermediary.
But if you do want to use a data table first, you could use LINQ:
var list = dt.AsEnumerable().Select(r => new Person() {
Name = (string) r["Name"],
Age = (int) r["Age"] }
).ToList()
or just itterate of dt.Rows and create a new person and add it to the list
You should also use Using() statements around your connection and reader.
There's also Linq to DataSet that you could use to do something along these lines
var list = (from tr in dt.AsEnumerable()
select new Person() {
Name = tr.Field<string>("Name"),
Age = tr.Field<int>("Age")
}).ToList();
Robert beat me to the answer, just using slightly different syntax.
In addition to the other options presented, you could defer the execution until needed with a yield:
public static IEnumerable<Person> GetPeople()
{
using( var conn = GetConnection() )
{
conn.Open();
string sql = "SELECT name, age FROM person";
var cmd = new SqlCommand( sql, conn );
using( SqlDataReader rdr = cmd.ExecuteReader() )
{
if( rdr == null )
{
throw new NullReferenceException( "No People Available." );
}
while( rdr.Read() )
{
var person = new Person();
person.Name = rdr["name"].ToString();
person.Age = Convert.ToInt32 ( rdr["age"] );
yield return person;
}
}
}
}
First, you'll want to make your Name and Age fields non-static (so each instance of the class will have their own values for these properties.
Then you could do something like this:
foreach(DataRow row in dt.Rows){
Person p = new Person(){
Name = Convert.ToString(row["name"]),
Age = Convert.ToInt32(row["age"])
}
person.Add(p);
}
Hope this helps!
Yes you can. Iterate through the items in dt.Rows and convert them manually to Person objects.
A user recently asked me a question on converting a DataTable to a List<> in .NET 2.0. Here’s the code to do so:
C#
// Assuming there is a DataTable called dt
List<DataRow> drlist = new List<DataRow>();
foreach (DataRow row in dt.Rows)
{
drlist.Add((DataRow)row);
}
Related
I want to serialize the DataTable to a List in C#. My code is taking around 10 minutes for 100K data to fetch and convert to a list. Previously tried with sequential way but it three times more time to process. So, I tried Parallel and it saved 1/3 of time. But, still, it's too slow.
If anyone can help to make it fast. Any help is appreciated.
public async Task<List<DataPointModel>> GetDetails(HashParameterModel parameterModel)
{
List<DataPointModel> result = new List<DataPointModel>();
try
{
var query = "USP_GetDetailsFromMetaData";
using (var sqlConnection = new SqlConnection(connectionString))
{
using (var sqlCommand = new SqlCommand(query, sqlConnection))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.AddWithValue("#GroupID", parameterModel.GroupID);
sqlCommand.CommandTimeout = 0;
sqlConnection.Open();
DataTable dataTable = new DataTable();
DataSet dataSet = new DataSet();
SqlDataAdapter da = new SqlDataAdapter
{
SelectCommand = sqlCommand
};
da.Fill(dataSet);
dataTable = dataSet.Tables[0];
DataTable dataTable1 = dataSet.Tables[1];
var questionList = dataTable1.AsEnumerable();
if (dataTable.Rows.Count > 0)
{
Parallel.ForEach(dataTable.AsEnumerable(), new ParallelOptions { MaxDegreeOfParallelism = 5 }, row =>
{
DataPointModel model = new DataPointModel();
model.ID = Convert.ToInt32(row["ID"]);
model.GroupID = Convert.ToInt32(row["GroupID"]);
model.ProviderID = Convert.ToInt32(row["SalvageProviderID"]);
model.ClaimNumber = row["ClaimNumber"].ToString();
model.PolicyNumber = row["PolicyNumber"].ToString();
model.DataPoint1 = row["DataPoint1"].ToString();
model.DataPoint2 = row["DataPoint2"].ToString();
model.DataPoint3 = row["DataPoint3"].ToString();
model.DataPoint4 = row["DataPoint4"].ToString();
model.FirstName = row["FirstName"].ToString();
model.LastName = row["LastName"].ToString();
model.PrimaryDamage = row["PrimaryDamage"].ToString();
model.Type = row["TypeCode"].ToString();
model.LossDate = row["LossDate"].ToString();
model.QuestionList = (from p in questionList
where p.Field<int>("ID") == model.ID
select new QuestionResponseModel()
{
QuestionID = p.Field<int>("QuestionID").ToString(),
Response = p.Field<string>("ResponseValue")
}).ToList();
result.Add(model);
});
}
}
}
}
catch (Exception ex)
{
throw ex;
}
return result;
}
The DataSet has two DataTable
DataTable dataTable = dataSet.Tables[0]; // Details
DataTable dataTable1 = dataSet.Tables[1]; // QUestionList
I think the time is consumed when its looping interlly for QuestionList, which may have around 120K rows. Any Suggestions
An easy and quick to implement performance improvment would be to build a look up table from your questionList, and access this to fetch the question instead of doing this piece of code
model.QuestionList = (from p in questionList
where p.Field<int>("ID") == model.ID
select new QuestionResponseModel()
{
QuestionID = p.Field<int>("QuestionID").ToString(),
Response = p.Field<string>("ResponseValue")
}).ToList();
So add the following
var questionList = dataTable1.AsEnumerable();
//maybe add .AsParallel() - questionList.AsParallel().ToLookUp(...)
var questionLookUp = questionList.ToLookUp(x => x.Field<int>("ID"), x => new QuestionResponseModel() { QuestionID = x.Field<int>("QuestionID"), Response = p.Field<string>("ResponseValue") });
And than use it like this
model.QuestionList = questionLookUp[model.ID].ToList();
https://learn.microsoft.com/en-us/dotnet/api/system.linq.lookup-2
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tolookup
Code is untested, hope I didn't make to many errors.
Are there duplicate model IDs in the Details table? If so this could help to avoid running the question list query multiple times:
model.QuestionList = getQuestions(model.ID);
Method:
public Dictionary<int, List<QuestionResponseModel>> questionBuffer = new Dictionary<int, List<QuestionResponseModel>>();
public List<QuestionResponseModel> getQuestions(int ID)
{
if (questionBuffer.ContainsKey(ID)) return questionBuffer[ID];
List<QuestionResponseModel> questions = (from p in questionList
where p.Field<int>("ID") == model.ID
select new QuestionResponseModel()
{
QuestionID = p.Field<int>("QuestionID").ToString(),
Response = p.Field<string>("ResponseValue")
}).ToList();
questionBuffer.Add(ID, questions);
return questions;
}
I'm creating a list <object> and here I'm adding my data from a database.
But I totally don't know how to deal with a list <object>.
public class ClassList
{
public int Name { get; set; }
public string Birthday { get; set; }
}
private void GetClassList()
{
SqlConnection conn = new SqlConnection(GlobalVar.ConnString);
SqlCommand cmd = new SqlCommand
("SELECT * " +
"FROM Class_Data ", conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
try
{
List<ClassList> ClassList = new List<ClassList>();
while (dr.Read())
{
ClassList.Add(new ClassList()
{
Name = ___?___, //what should i write here
Birthday = ___?___ //and here
});
}
}
catch{}
finally
{
dr.Close();
conn.Close();
}
}
Having been looking for lots of information but still fail to work it out.
I will be very appreciated if someone could help!
This should do it:
List<ClassList> ClassList = new List<ClassList>();
while (dr.Read())
{
ClassList.Add(new ClassList()
{
Name = (int)dr["Name"], //the name of the db column
Birthday = dr["Birthday"].ToString();
});
}
As a side note, it maybe worth altering your query from SELECT *... to just selecting the columns you require, from readability/maintenance point of view.
Also, is Name really an int seems like it could be a string.
You can also refer to the documentation:
SqlDataReader.Item Property (String)
You can use column name that are return by your database query with dr like dr["ColumnName"]
ClassList.Add(new ClassList()
{
Name = dr["NameColum"], //what should i write here
Birthday = dr["BrithColum"] //and here
});
Something like that:
// You don't use "this" in the context
// It seems, that you want to return the collected data (List<ClassList>)
private static List<ClassList> GetClassList() {
// Put IDisposable into using
using (SqlConnection conn = new SqlConnection(GlobalVar.ConnString)) {
conn.Connect();
// You want to fetch two fields only, right? Not the entire table (*)
String sql =
#"select Name,
BirthDay
from Class_Data"
using (SqlCommand cmd = new SqlCommand(sql, conn)) {
List<ClassList> result = new List<ClassList>();
using (SqlDataReader dr = cmd.ExecuteReader()) {
while (dr.Read()) {
result.Add(new ClassList() {
Name = Convert.ToString(dr[0]),
Birthday = Convert.ToDateTime(dr[1])
});
}
}
return result;
}
}
}
You can access the column values with dr["ColumnName"].
Hopefully i don't sound confusing but i am not sure if what i am trying to get at is possible.
I have a select statement to get name, id, guid. I am setting the display to name and the value to Id for each combobox. Is there a way that i could also assign the guid to the combo box so that i could use it in my winforms app?
here is what i have for select statement:
private void secondChild_drp_SelectedIndexChanged(object sender, EventArgs e)
{
string secondChildId = secondChild_drp.SelectedValue.ToString();
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlDataAdapter sda = new SqlDataAdapter("SELECT ... WHERE em.ChildID = (" + secondChildId + ")", conString))
{
DataTable dt = new DataTable();
sda.Fill(dt);
thirdChild_drp.ValueMember = "ID";
thirdChild_drp.DisplayMember = "DisplayName";
thirdChild_drp.DataSource = dt;
}
}
cmd.CommandText="StoreProcName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ChildID", secondChildId);
cmd.Connection = con2;
con2.Open();
reader = cmd.ExecuteReader();
var guid = reader.ToString();
reader.Close();
con2.Close();
}
right now when i run this it tells me reader = cmd.ExecuteReader(); has Procedure or function StoreProcName has too many arguments specified.
i just want to get the guid associated with the id i passed in.
You can get the guid from your datatable as follows where yourselectedid is the combobox selecteditem id.
var results = from row in dt.AsEnumerable()
where row.Field<int>("ID") == yourselectedid
select row;
now from results you can get all the desired columns you want
Basically the same answer as I already posted here:
You could define a simple object which you are filling from your data base query:
public class Item
{
public int ID { get; set; }
public string DisplayName { get; set; }
public Guid Guid{ get; set; }
}
Your implementation could look something like this (some mockup data):
listBox1.DataSource = items;
listBox1.DisplayMember = "DisplayName";
listBox1.ValueMember = "ID";
Then based on the value you selected, you can query through your items and get the item:
var key = (int)listBox1.SelectedValue;
foreach (var existingItem in items)
{
if (existingItem.Key == key)
{
//woohoo got it!
Debug.Print(existingItem.Guid.ToString())
}
}
you can put both of the value in the value member, separated by whichever character for separator like : "12;0000-000-0000" then separate again the Value Menber with a String.Split.
I am getting data from SQL and putting it in list. here's what I am trying now,
public class Fruit //custom list
{
public string aID { get;set; } // can be more then 1
public string bID { get;set; } // only 2 but different aID
public string name { get;set; } // only 1 for selection of aID and bID
}
and this is how i am getting data from sql,
var Fruitee = new Fruit();
using (SqlConnection cn = new SqlConnection(CS()))
{
cn.Open();
SqlCommand sqlCommand= new SqlCommand("SELECT * FROM myTable", cn);
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
Fruitee.add(reader["aID"], reader["bID"],reader["name"]) // ??? not sure what to put here as add is not available
}
cn.Close();
}
Table looks like this,
aID, bID, name
**
Problem
**
I am stuck how to add items to list and also is it best practice ?
List<Fruit> fruits = new List<Fruit>();
while (reader.Read())
{
Fruit f = new Fruit();
f.aID = (string) reader["aID"];
f.bID = (string) reader["bID"];
f.name = (string) reader["name"];
fruits.Add(f);
}
var list = new List<Fruit>();
while (reader.Read())
{
list.Add(new Fruit() { aID = reader["aID"].ToString(), bID = reader["bID"].ToString(), name = reader["name"].ToString() });
}
var Fruitee = new List<Fruit>();
while (reader.Read())
{
Fruitee.Add(new Fruit() { aID = reader["aID"].ToString(), bID = reader["bID"].ToString(), name = reader["name"].ToString() });
}
You need to actually read from the reader and that's what you're not doing. However, be aware of DBNulls.
So assuming that your custom list has an add function you could do something like the following.
while(reader.Read())
{
Fruitee.add(reader["aID"], reader["bID"], reader["name"]);
}
I have one array list:
public ArrayList GetExpenseTypes()
{
ArrayList expArry = new ArrayList();
using (SqlConnection conn = new SqlConnection(connStr))
{
string sql = "SELECT ID, TITLE";
sql += " FROM EXPENSE_TYPE";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader reader;
try
{
conn.Open();
reader = cmd.ExecuteReader();
while (reader.Read())
{
expArry.Add(new ListItem(reader["TITLE"].ToString(), reader["ID"].ToString()));
}
reader.Close();
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
}
return expArry;
}
My ArrayList like this
1 name1
2 name2
3 name3
4 name4
5 name6
if my value is 4 i need to display name4
How i achieve that?
Instead of ArrayList you might want to use Dictionary<string,string> like this.
public IDictionary<string,string> GetExpenseTypes()
{
Dictionary<string,string> expArry = new Dictionary<string,string>();
// Your sql code
while (reader.Read())
{
expArry.Add(reader["TITLE"].ToString(), reader["ID"].ToString());
}
// The rest of your code
}
Then you can get the value like this
var values = GetExpenseTypes();
string valueYouWant = values["4"];
If on the other hand your problem is that when you use the ListItems in a web control you are seeing the wrong values, then you need to swap the parameters when you create the ListItem because the first parameter is the text that is displayed and the second is the value. Like this.
expArry.Add(new ListItem(reader["ID"].ToString(), reader["TITLE"].ToString()));
In which case you should consider using a List<ListItem> instead of ArrayList
you could use BinarySearch method if you are searching for value types; in your case this does not seem possible.
I think you may need to use a loop assuming that you can not use Linq (because of the framework employed);
int index = -1;
for(int i=0; i<expArray.Count;i++)
{
ListItem temp = (expArray[i] as ListItem);
if(temp != null && temp.Value == "some value")
{
index = i;
break;
}
}
I'd recommend first changing ArrayList to List<ListItem>. If you can't do that for some reason you could use (assuming each item is unique):
var x = expArry.Cast<ListItem>().SingleOrDefault(exp => exp.Value == value);
if (x != null)
//item was found
If you know the item will always be there just use Single if it will only be there once.
Don't use ArrayList, do something like this using a generic dictionary
public IDictionary<int, string> GetExpenseTypes()
{
var result = new Dictionary<int, string>();
using (var conn = new SqlConnection(connStr))
{
var getExpenses = new SqlCommand(
"SELECT ID, TITLE FROM EXPENSE_TYPE",
conn);
conn.Open();
using (var reader = command.ExecureReader())
{
while (reader.Read())
{
result.Add(reader.GetInt32(0), reader.GetString(1));
}
}
}
return result;
}
Then you could look up your string like this
var type4 = GetExpenseTypes()[4];
alternatively, don't get the whole list to find one value.
public string GetExpenseType(int id)
{
using (var conn = new SqlConnection(connStr))
{
var getExpenseType = new SqlCommand(
"SELECT TITLE FROM EXPENSE_TYPE WHERE ID = #p0",
conn);
getExpenseType.Parameters.Add(id);
conn.Open();
return (string)getExpenseType.ExecuteScalar();
}
}
If, for some bad reason, you want to stick with your ArrayList returning function, you can find your item like this.
var expenseTypes = GetExpenseTypes().OfType<ListItem>().ToDictionary(
li => int.Parse(li.Text),
li => li.Value);
var type4 = expenseTypes[4];
or, if you want to do this once.
var type4 = GetExpenseTypes().OfType<ListItem>()
.Single(li => li.Text == "4").Value;