I'm having a problem outputting to a WebGrid because my list gets overwritten, so by the end, I have the final line of data written for every line on the grid. I have to use a while loop because the data is continually being added to, and we're looking at alot of data, so I'm trying not to write to another list.
public class ChemData
{
string strSQLconnection = "Server=Server;Database=data;Uid=Username;Pwd=Password";
public int productId { get; set; }
public string productName { get; set; }
public List<ProdData> ProdList = new List<ProdData>();
public List<ProdData> ProdDataPull()
{
ProdData Analysis = new ProdData();
SqlDataReader reader = null;
SqlConnection conn = new SqlConnection(strSQLconnection);
SqlCommand query = new SqlCommand("Select * from producttable");
conn.Open();
query.Connection = new SqlConnection(strSQLconnection);
query.Connection.Open();
reader = query.ExecuteReader();
while (reader.Read())
{
if (!reader.IsDBNull(0)) Analysis.productId = reader.GetInt32(0);
if (!reader.IsDBNull(1)) Analysis.productName = reader.GetString(1);
ProdList.Add(Analysis);
}
return ChemList;
}
}
This is because Analysis is created only once. Adding it to the list each time adds a reference to the same object. Moving the creation to inside the while loop should fix this
while (reader.Read())
{
ProdData Analysis = new ProdData();
if (!reader.IsDBNull(0)) Analysis.productId = reader.GetInt32(0);
if (!reader.IsDBNull(1)) Analysis.productName = reader.GetString(1);
ProdList.Add(Analysis);
}
This creates a new ProdData object on each iteration of the loop and assigns it to Analysis, then updates its contents and adds that reference to the list.
With the creation of Analysis outside the loop Analysis continues to point to the same ProdData object that gets added over and over to the list while its values are overwritten each time.
See : Reference Types vs. Value Types
Related
I am using a SqlDataReader to fetch data from a stored procedure. Even though the records are being fetched, the while (reader.Read()) gets executed only once, and so in my list only one row is added.
List<Student> tablelist = new List<Student>();
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("SP_ReadPromotedStudents"))
{
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = Data[0];
cmd.Parameters.Add("#Email", SqlDbType.VarChar).Value = Data[1];
cmd.Parameters.Add("#Class", SqlDbType.VarChar).Value = Data[2];
con.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.HasRows)
{
while (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
reader.NextResult();
}
}
}
}
}
return tablelist;
My Student class:
public class Student
{
public string Name { get; set; }
public string Email { get; set; }
public string Class { get; set; }
}
I have about 46 records being fetched. But in the list only one record gets added. What is the mistake here?
You need to move your call to NextResult outside the reader.Read() loop. Otherwise after the first read the code encounters the NextResult call and tries to load a second sets of data returned by the stored procedure.
Also the loop over HasRows is an infinite loop. If the property reader.HasRows is true it will be true also when you finish to read the rows.
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
}
// This should be called only if your stored procedure returns
// two or more sets of data otherwise you can remove everything
reader.NextResult();
// If there is another set of data then you can read it with a
// second while loop with
while(reader.Read())
{
.....
}
}
The ideal scenario would to have a new sql statement to get just what you want instead the get a list and need just the first access. Imagine if you have a table with millions of records, would you need to execute a query to get all and read just the first one? No, you execute a query to get the you need.
The NextResult method from DataReader moves the pointer to the next result if you have it on the result. Remove it.
After you chanfge the sql statement to get what you need, you are looping the result set. You could read just the first line (changing the while to if):
if (reader.Read())
{
tablelist.Add(new Student
{
Name = (string)(reader[0]),
Email = (string)(reader[1]),
Class = (string)(reader[2]),
});
}
I am making a list of elements, but each time it iterates modify all the elements of the list
I am executing the code step by step and i added and inspection, then i realized that the data that i am retreiving is ok, but when i add an "element" to the list, it modifies all the existents elements
I have this class
public class AREntity
{
public DateTime? FI { get; set; }
public DateTime? FS { get; set; }
}
And this is the code that i am executing ....
List<AREntity> list = new List<AREntity>();
MySqlConnection Conn = new MySqlConnection();
Conn.ConnectionString = sqlCnn;
Conn.Open();
try
{
String CSql = "LAR";
MySqlCommand cmd = new MySqlCommand(CSql, Conn);
cmd.CommandType = CommandType.StoredProcedure;
MySqlDataReader drAR = cmd.ExecuteReader();
AREntity ARE = new AREntity();
while (drAR.Read())
{
ARE.FI = drAR.GetValue(2);
ARE.FS = drAR.GetValue(3);
list.Add(ARE);
}
when the code executes
list.Add(ARE);
the entire list change
You are adding the same AREntity instance to the list and just changing its values. Move the object creation inside the loop:
while (drAR.Read())
{
AREntity ARE = new AREntity();
ARE.FI = drAR.GetValue(2);
ARE.FS = drAR.GetValue(3);
list.Add(ARE);
}
I do not know what I am doing wrong, when I add the values from my database to a list<object> it always returns the list with the total of objects but all the values are those of the last record that was made in the loop while.
This is my code:
public List<object> getdata(string storedProcedure)
{
List<object> list = new List<object>();
try
{
using (var conn = new NpgsqlConnection(connstring))
{
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlDataReader reader;
var cmd = new NpgsqlCommand(storedProcedure, conn);
cmd.CommandType = CommandType.StoredProcedure;
reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
object[] fieldValues = new object[fieldCount];
while (reader.Read())
{
int instances = reader.GetValues(fieldValues);
for (int fieldCounter = 0; fieldCounter < fieldCount; fieldCounter++)
{
if (Convert.IsDBNull(fieldValues[fieldCounter]))
fieldValues[fieldCounter] = "NA";
}
list.Add(fieldValues);
}
reader.Close();
tran.Commit();
conn.Close();
return list;
}
}
catch (Exception ex)
{
}
return list;
}
This is what I get in all positions, it is the last value:
You need to move the declaration and initialization of your object array inside the loop
....
while (reader.Read())
{
object[] fieldValues = new object[fieldCount];
....
}
The problem that you experience is caused by the fact that when you initialize the array outside the loop and reuse it at every loop of the datareader, you replace the previous content with the content of the current record.
But when you add the array to the list of objects you add the same reference to initial array where only the content has been changed. Obviously when you reach the last record there is only one array while the list contains n reference to the same memory area. So you display n time the same last record.
Moving the initialization inside the loop provides the list with a new reference at each loop and each reference maintain the data of the record received during the loop.
At the moment, I have this which works fine:
using (connection = new SqlConnection("connection string here"))
{
using (command = new SqlCommand(#"select * from tbl1", connection))
{
connection.Open();
using (reader = command.ExecuteReader())
{
while (reader.Read())
{
int ColIndex1 = reader.GetOrdinal("col_1");
int ColIndex2 = reader.GetOrdinal("col_2");
Console.Write(reader.GetString(ColIndex1);
Console.Write(" - ");
Console.Write(reader.GetString(ColIndex2);
Console.Write(Environment.NewLine);
}
}
}
}
I have another query which I run separately, but that second query needs the first query, which means I end up running the first query twice. To avoid that, if I changed the command line to:
using (command = new SqlCommand(#"select * from tbl1; select * from tbl2", connection))
How do I get each query into a separate list? I understand how to get a single query into a list, i.e:
public class Data
{
public int ColumnIndex1 { get; set; }
public int ColumnIndex2 { get; set; }
}
List<Data> list = new List<Data>();
list.Add(new Data(ColIndex1, ColIndex2));
The first query is used to create directories on the hard drive. The second query then uses the first query and then adds files to the created directories.
using (connection = new SqlConnection("connection string here"))
{
using (command = new SqlCommand(#"select * from tbl1", connection))
{
connection.Open();
using (reader = command.ExecuteReader())
{
while (reader.Read())
{
// read first grid
}
if(reader.NextResult())
{
while (reader.Read())
{
// read second grid
}
}
}
}
}
However, I strongly suggest using helper tools, for example, via "dapper":
List<FirstType> first;
List<FirstType> second;
using(var multi = connection.QueryMultiple(sql, args))
{
first = multi.Read<FirstType>().ToList();
second = multi.Read<SecondType>().ToList();
}
I think you need to investigate the NextResult method on the IDataReader interface. This allows you to move through multiple result sets.
http://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx
I have in my database a table called students that have the number and name, address....
I have a form where I load all information for one student at a a time , and I have a next button and a back button.
How can I iterate to the next row (or previous row) in mysql (to be able to see the info of the next student) ?
I tried to use the primary key (auto increment) to iterate and when I want to see the next record I add 1 to the id or subtract 1 to see the previous record.
But if one record is deleted it will show an empty record.
Can you point me in the rigth direction?
I´m using WinForms
Sorry about my english..
string config = "server=localhost; userid = root; database = databaseName";
MySqlConnection con = new MySqlConnection(config);
MySqlDataReader reader = null;
string query = "SELECT * FROM students WHERE id = " + id; //id is the primary Key (auto increment)
MySqlCommand command = new MySqlCommand(query, con);
con.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
string studentName = (string)reader["studentName"];
string studentNum = (string)reader["studentNum"];
tbstudentName.Text = Convert.ToString(studentName);
tbstudentNum.Text = Convert.ToString(studentNum);
.....
}
con.Close();
You should not be calling the database each time you want to view the next record. Try reading all the data into a List.
I am not sure what you are using.. WinForms? WPF?
If WinForms you will need to do something like this.
public class Student
{//First create a class to hold your data in
public string Name { get; set; }
public string Num { get; set; }
}
public class MyForm : Form
{
int Index = 0;
List<Student> FormData { get; set; }
void GetData()
{
//This will hold all your data in memory so you do not have to make a database call each and every "iteration"
List<Student> dbData = new List<Student>();
string config = "server=localhost; userid = root; database = databaseName";
MySqlConnection con = new MySqlConnection(config);
MySqlDataReader reader = null;
string query = "SELECT * FROM students";
MySqlCommand command = new MySqlCommand(query, con);
con.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
Student newStudent = new Student();
newStudent.Name = (string)reader["studentName"];
newStudent.Num = (string)reader["studentNum"];
//Add data to the list you created
dbData.Add(newStudent);
.....
}
con.Close();
//set the Form's list equal to the one you just populated
this.FormData = dbData;
}
private void BindData()
{
//If winforms
tbstudentName.Text = FormData[Index].Name;
tbstudentNum.Text = FormData[Index].Num;
//If wpf you will have to use view models and bind your data in your XAML but I am assuming you are using
//winforms here.
}
private void NextRecord()
{ //If you reached the end of the records then this will prevent IndexOutOfRange Exception
if (Index < FormData.Count - 1)
{
Index++;
BindData();
}
}
private void PreviousRecord()
{
if (Index != 0)
{
Index--;
BindData();
}
}
}
Now the above scenario will get it working quickly; however, there are better ways in doing this that would help you when you need to alter that data. I would recommend WinForms Binding. You can check it out here http://msdn.microsoft.com/en-us/library/c8aebh9k(v=vs.110).aspx
To get the next you can write:
select * from students where id > #id
order by id asc
limit 1
And to get previous
select * from students where id < #id
order by id desc
limit 1
DataReader Designed to quick one-time read.
If you want to hold the data, you need to fill memory arrays.
the DataTable implements it very well.
You will need to think a little different.
Getting id+1 you are being very careless.. Even identity, the Id can be another value and you will get an Exception.. I suppose that you don't want it.
You will need to Adjust your logic to return lines with top or, in mysql, limit statement..
This will be easy using lambda to use .Take() and Skip() methods...
You also can use the limit parameter to pass throug this sample.. you can understand..
MySQL skip first 10 results
Hope it helps.