How do I fill a DataTable using DataReader - asp.net

I want to fill DataTable using DataReader.
I have created object like this
SqlDataReader dr = cmd.ExecuteReader();
if(dr.HasRows)
{
}

If all you want is a ReadOnly DataTable for reporting or web, try this:
conn = new SqlConnection(connString);
string query = "SELECT * FROM Customers";
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
DataTable dt = new DataTable();
dt.Load(dr);
Credit where it's due: http://www.dotnetcurry.com/showarticle.aspx?ID=143

DataTable.load() can be used for a generic approach.
do {
var table = new DataTable();
table.Load(reader);
dataset.Tables.Add(table);
} while(!reader.IsClosed);

You can get the Schema Table from your SqlDataReader dr to get the column names, save the names to a List<string> and add them as columns on a new DataTable, then fill that DataTable using indexing on dr with the names from the list:
DataSet ds = new DataSet();
DataTable dtSchema = dr.GetSchemaTable();
DataTable dt = new DataTable();
List<DataColumn> listCols = new List<DataColumn>();
List<DataColumn> listTypes = new List<DataColumn>();
if (dtSchema != null)
{
foreach (DataRow drow in dtSchema.Rows)
{
string columnName = System.Convert.ToString(drow["ColumnName"]);
DataColumn column = new DataColumn(columnName, (Type)(drow["DataType"]));
listCols.Add(column);
listTypes.Add(drow["DataType"].ToString()); // necessary in order to record nulls
dt.Columns.Add(column);
}
}
// Read rows from DataReader and populate the DataTable
if (dr.HasRows)
{
while (dr.Read())
{
DataRow dataRow = dt.NewRow();
for (int i = 0; i < listCols.Count; i++)
{
if (!dr.IsDBNull[i])
{
// If your query will go against a table with null CLOB fields
// and that column is the 5th column...
if (strSQL == "SELECT * FROM TableWithNullCLOBField" && i == 4)
dataRow[((DataColumn)listCols[i])] = dr.GetOracleClob(i).Value;
// If you might have decimal values of null...
// I found dr.GetOracleDecimal(i) and dr.GetDecimal(i) do not work
else if (listTypes[i] == System.Decimal)
dataRow[((DataColumn)listCols[i])] = dr.GetFloat(i);
else
dataRow[((DataColumn)listCols[i])] = dr[i]; // <-- gets index on dr
}
else // value was null
{
byte[] nullArray = new byte[0];
switch (listTypes[i])
{
case "System.String":
dataRow[((DataColumn)listCols[i])] = String.Empty;
break;
case "System.Decimal":
case "System.Int16": // Boolean
case "System.Int32": // Number
dataRow[((DataColumn)listCols[i])] = 0;
break;
case "System.DateTime":
dataRow[((DataColumn)listCols[i])] = DBNull.Value;
break;
case "System.Byte[]": // Blob
dataRow[((DataColumn)listCols[i])] = nullArray;
break;
default:
dataRow[((DataColumn)listCols[i])] = String.Empty;
break;
}
}
}
dt.Rows.Add(dataRow);
}
ds.Tables.Add(dt);
}
// Put this after everything is closed
if (ds.Tables.Count > 0)
return ds.Tables[0]; // there should only be one table if we got results
else
return null;
Obviously you'd need your try...catch...finally block around it all to handle exceptions and disposing your connection, and use the last condition after the finally. I found this helpful in order to handle finding out when I had results or not, and avoided issues with dt.Load(dr) that was failing when there were no results. ds.Fill(adapter) wasn't much better, as it failed when I tried to grab a table of 97 columns and about 80 rows with SELECT * FROM MyTable. Only the code above managed to work in all scenarios, for me.
Originally posted on Populate data table from data reader by sarathkumar. I provided the summary, condensed it, added the null checks and assigning if it's a null value, and added the table to a DataSet and added the DataSet condition at the end.
NOTE: For those using OracleDataReader, I found out that you can experience an error if you have an NCLOB or CLOB field that is null in the table/results set that you are reading. I found if I checked for that column by looking at the index i and did dr.GetOracleClob(i) instead of dr[i], I stopped getting the exception. See answer at EF + ODP.NET + CLOB = Value Cannot be Null - Parameter name: byteArray? and I added this condition in the code above when if (!dr.IsDBNull[i]). Similarly, if you have a null Decimal field, I had to check it with dr.GetFloat(i);, since neither dr.GetOracleDecimal(i); and dr.GetDecimal(i); seemed to correctly accommodate for a null value.

To fill a DataSet, you can use something like:
var da = new SqlDataAdapter();
da.SelectCommand = cmd; // your SqlCommand object
var ds = new DataSet();
da.Fill(ds);

Related

Error: There is no row at position 0 in asp.net

I wrote this code to get data from gridview to display down the controls, but it failed:
There is no row at position 0.
at line:
txtSTT.Text = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(0).ToString();
Full code:
protected void btnSua_Click(object sender, EventArgs e)
{
string STT = ((LinkButton)sender).CommandArgument;
SqlConnection conn = new SqlConnection(#"Data Source=DESKTOP-R8LG380\SQLEXPRESS;Initial Catalog=PHIM;Integrated Security=True");
string query = "SELECT * FROM Trailer WHERE STT = '" + STT + "'";
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "Trailer");
txtSTT.Text = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(0).ToString();
txtMaTrailer.Text = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(1).ToString();
cmbMaPhim.SelectedValue = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(2).ToString();
txtUrlTrailer.Text = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(3).ToString();
txtGhiChu.Text = ds.Tables["Trailer"].Rows[0].ItemArray.GetValue(4).ToString();
txtSTT.ReadOnly = true;
txtSTT.Visible = true;
lbSTT.Visible = true;
}
From the error message, I would say your query is not returning any rows.
Try putting a break point right after the assignment to the queryand copy the string into a query window in SQL Server Management Studio to see if any rows of data come back.
It is probably not returning any rows. Debug your query until you know you have one that returns data.
Also, In addition to putting the contents of this function in a try/catch block, I would wrap all the lines that use ds.Tables["Trailer"].Rows[0] in an if block, something like if(ds.Tables["Trailer"].Rows.Count > 0){

loop returning inadequete records from database

I have a list that is taking data from a database. It is always leaving one record that meets my database search query. when i change the database details to increase or decrease the records, it still leaves one record. out of say 8 records that meets a search criteria it returns 7, out of say 5 it returns 4
using (SqlConnection con = new SqlConnection("Data Source=*** Initial Catalog=***y;Persist Security Info=True;User ID=***;Password=***"))//connection string
{
con.Open();
SqlCommand cmd = new SqlCommand("SELECT SubjectName FROM dbo.mySubjects WHERE SubjectClass = #theSubjectClass", con);//query string
cmd.Parameters.Add("#theSubjectClass", SqlDbType.VarChar).Value = myClass;
SqlDataReader dr = cmd.ExecuteReader();
dr.Read();
//int t = 0;
//count = count + 1; Tried adding a counter that gets the number of raws in the table that meet the search query and create a loop based on that but it produces an error of trying to read data where there is no data to read
while ( count >= 0) {
dr.Read();
List <string> Subjects = new List<string>();
Subjects.Add(dr.GetString(0));
//t = 0;
foreach (var course in Subjects)
{
var a = new HtmlGenericControl("a");
a.Attributes["class"] = "color-info";
var container = new HtmlGenericControl("div");
container.Attributes["class"] = "col-lg-4 col-sm-6";
var div = new HtmlGenericControl("div");
div.Attributes["class"] = "card card-warning wow zoomInUp animation-delay-5";
var text = new HtmlGenericControl("div");
text.Attributes["class"] = "card-block text-center";
var p = new HtmlGenericControl("p");
text.Controls.Add(p);
string manage = dr.GetValue(0) + " Videos";
var btn = new Button
{
Text = manage,
CssClass = "btn btn-warning"
};
btn.Click += new EventHandler(ExploreButtonClick);
text.Controls.Add(btn);
div.Controls.Add(text);
container.Controls.Add(div);
a.Controls.Add(container);
Row.Controls.Add(a);
count = count - 1;
}
}
con.Close();
}
Your while loop is incorrect. It should be:
using(var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
....<your code here>
}
}
Of course in this version you no longer need this List <string> Subjects collection and foreach (var course in Subjects) loop to repack it.

search data from database asp.net

protected void Button1_Click(object sender, EventArgs e)
{
string db = "Data Source=DESKTOP-R6H3RTP;Initial Catalog=AdmitDB; Integrated Security= true;";
SqlConnection mycon = new SqlConnection(db);
mycon.Open();
String query = "select * from tblPatient where PhoneNo like '"+TextBox1.Text+"%'";
SqlCommand cmd = new SqlCommand(query, mycon);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
if (cmd.ExecuteNonQuery() > 0)
{
lblName.Visible = true;
lblId.Visible = true;
lblPNo.Visible = true;
lblDOB.Visible = true;
lblName.Text = "PName";
lblId.Text = "Pid";
lblPNo.Text = "PhoneNo";
lblDOB.Text = "PDOB";
}
else
{
lblNotFound.Visible = true;
}
}
i'm searching from database but just else statement executes don't know why it's not get data from database, if any kind of error then help me please
i think you don't need if (cmd.ExecuteNonQuery() > 0). the cmd executes automatically. you want to check the tables in the dataset.
// check the first table for rows.
if(ds.Tables[0].HasRows())
{
// success. now you can work with the table.
}
ExecuteNonQuery method returns the number of row that were modified by the query. Since SELECT query doesn't modify anything in the database - you get 0.
You should modify your query with a COUNT(*) function:
String query = "select COUNT(*) from tblPatient where PhoneNo like '"+TextBox1.Text+"%'";
Then you can get that value with ExecuteScalar():
if (cmd.ExecuteScalar() > 0)

Populate web form edit page C# ASP.NET

I am trying to select a single row on a gridview and have that selection take me to a separate edit page with the data populated. I have the idea of using a session variable to hold the row id and then retrieving the data on page load and populating the text boxes. My question is whether or not this is the best method to go about doing this? I would prefer to not use the inline edit option in gridview as I have too many columns that would require scrolling horizontally. Here is my page load method using the session variable:
if (Session["editID"] != null)
{
dbCRUD db = new dbCRUD();
Recipe editRecipe = new Recipe();
var id = Convert.ToInt32(Session["editID"]);
Session.Remove("editID");
editRecipe = db.SelectRecord(id);
addName.Text = editRecipe.Name;
}
Here is the SelectRecord method that is used to retrieve the row:
public Recipe SelectRecord(int id)
{
Recipe returnedResult = new Recipe();
var dbConn = new SqlConnection(connString);
var dbCommand = new SqlCommand("dbo.selectRecipe", dbConn);
dbCommand.CommandType = CommandType.StoredProcedure;
dbCommand.Parameters.Add("#ID", SqlDbType.Int).Value = id;
dbConn.Open();
SqlDataReader reader = dbCommand.ExecuteReader();
while (reader.HasRows)
{
while (reader.Read())
{
returnedResult.Name = reader["Name"].ToString();
}
}
dbConn.Close();
return returnedResult;
}
I'm probably not utilizing the SQLDataReader appropriately, but my result is no data in the reader therefore no returned data when calling the method. Any help is appreciated - thanks in advance!
Few things you should be aware of here:
1.
You should use while (reader.HasRows) in case your stored procedure returns multiple resultsets. In that case you have to iterate through the result sets. See Retrieving Data Using a DataReader. So, if selectRecipe returns multiple resultsets (I am assuming this is not the case), change your code to this:
while (reader.HasRows)
{
while (reader.Read())
{
returnedResult.Name = reader["Name"].ToString();
}
reader.NextResult();
}
2.If selectRecipe returns single result set, change the while loop to if(){}:
if(reader.HasRows)
{
while (reader.Read())
{
returnedResult.Name = reader["Name"].ToString();
}
}
3. I would probably use using to manage the connection better (using Statement) :
public Recipe SelectRecord(int id)
{
Recipe returnedResult = new Recipe();
using (SqlConnection dbConn = new SqlConnection(connString))
{
var dbCommand = new SqlCommand("dbo.selectRecipe", dbConn);
dbCommand.CommandType = CommandType.StoredProcedure;
dbCommand.Parameters.AddWithValue("#ID", id);
dbConn.Open();
SqlDataReader reader = dbCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
returnedResult.Name = reader["Name"].ToString();
}
}
reader.Close();
}
return returnedResult;
}

How to pass a Session to a DataTable?

In my asp project I need to write all Items form the list box into database table using TVP. I have listbox, stored procedure (passing TVP - Table Valued Parameters). The main problem is that passing DataTable is NULL, but the Session isn't. I'll send you my code form aspx.cs file (only one block, if you'll nedd more, let me know).
protected void ASPxButton1_Click(object sender, EventArgs e)
{
//DataTable dts = Session["SelectedOptions"] as DataTable;
DataTable _dt;
//_dt = new DataTable("Items");
_dt = Session["SelectedOptions"] as DataTable;
_dt.Columns.Add("Id", typeof(int));
_dt.Columns.Add("Name", typeof(string));
foreach (ListEditItem item in lbSelectedOptions.Items)
{
DataRow dr = _dt.NewRow();
dr["Id"] = item.Value;
dr["Name"] = item.Text;
}
SqlConnection con;
string conStr = ConfigurationManager.ConnectionStrings["TestConnectionString2"].ConnectionString;
con = new SqlConnection(conStr);
con.Open();
using (con)
{
SqlCommand sqlCmd = new SqlCommand("TestTVP", con);
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = sqlCmd.Parameters.AddWithValue("#testtvp", _dt);
tvpParam.SqlDbType = SqlDbType.Structured;
sqlCmd.ExecuteNonQuery();
}
con.Close();
}
EDIT
Debugging Screenshot
You are not adding the new rows to the DataTable. You need something like this:
foreach (ListEditItem item in lbSelectedOptions.Items)
{
DataRow dr = _dt.NewRow();
dr["Id"] = item.Value;
dr["Name"] = item.Text;
_dt.Rows.Add(dr);
}
A secondary concern is where are you initialising Session["SelectedOptions"]? It appears that every time ASPxButton1 is clicked the code will try and add Id and Name columns to it, even if it already contains those two columns. It would seem more logical to do something like:
_dt = Session["SelectedOptions"] as DataTable;
if (_dt == null)
{
_dt = new DataTable("Items");
_dt.Columns.Add("Id", typeof(int));
_dt.Columns.Add("Name", typeof(string));
Session.Add("SelectedOptions", _dt);
}

Resources