NullReferenceException error when searching database table for entries - c#

I'm working on a relatively straight forward C# project that connects an Access Database via OleDb with various functions such as adding, deleting and editing records. Now, everything has worked fine up until trying to implement a database search.
The following line (in the searchbox code) throws up the exception during debug, but I am unsure what is actually a null value that is causing it to break.
ReturnedResults = DBDataSet.Tables["Movies"].Select("Title like '%" + Search + "%'");
So the goal is to search through the 'Movies' table in the database and find movies based on the user's input (stored in the 'Search' string).
The search box's code can be found below, and the database initialisation and connection is provided at the bottom.
private void SearchTextBox_Changed(object sender, EventArgs e)
{
string SearchString = SearchTextBox.Text.ToString();
int Results = 0;
DataRow[] ReturnedResults;
ReturnedResults = DataSet.Tables["Movies"].Select("Title like '%" + SearchString + "%'");
Results = ReturnedResults.Length;
if (Results > 0)
{
SearchResultsBox.Items.Clear();
for (int i = 0; i <= Results; i++)
{
DataRow Row;
Row = ReturnedResults[i];
SearchResultsBox.Items.Add(Row["Title"].ToString());
}
}
else
{
SearchResultsBox.Items.Clear();
MessageBox.Show("Error! No items found.");
}
}
For some context here is the main database initialisation/connection and the form load events:
public partial class BaseForm : Form
{
System.Data.OleDb.OleDbConnection Connection;
DataSet DataSet;
System.Data.OleDb.OleDbDataAdapter DataAdapter;
int MaxRows = 0;
int CurrentRow = 0;
public BaseForm()
{
InitializeComponent();
}
private void BaseForm_Load(object sender, EventArgs e)
{
Connection = new System.Data.OleDb.OleDbConnection();
Connection.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;DataSource=Movies.accdb";
Connection.Open();
DataSet = new DataSet();
string sq1 = "SELECT * From Movies";
DataAdapter = new System.Data.OleDb.OleDbDataAdapter(sq1, DBConnection);
DataAdapter.Fill(DBDataSet);
MaxRows = DataSet.Tables[0].Rows.Count;
DisplayRecord();
}

Multiple things can be null in your statement. DBDataSet can be null, or the table Movies doesn't exits. You may add following check before the line.
if(DBDataSet != null &&
DBDataSet.Tables.Count > 0 &&
DBDataSet.Tables["Movies"] != null)
{
ReturnedResults = DBDataSet.Tables["Movies"].Select("Title like '%" + Search + "%'");
//.... rest of your code
You may also try accessing the record against Tables[0] instead of Table Movies
EDIT:
For your next problem, mentioned in the comment, your for loop is executing till the length. It should be like:
for (int i = 0; i < Results; i++) // Less than (<) not less than equal to
{
DataRow Row;
Row = ReturnedResults[i];
SearchResultsBox.Items.Add(Row["Title"].ToString());
}
You need to modify the condition and remember the index starts from 0 and you will get rows till Result - 1

In your case multiple null reference is occur in your code as per your comment you got error in following line
ReturnedResults = DBDataSet.Tables["Movies"].Select("Title like '%" + Search + "%'");
In above DBDataSet may be null or its table (Movies) has no row etc.. so better way t check before filter
if(DBDataSet != null &&
DBDataSet.Tables.Count > 0 &&
DBDataSet.Tables["Movies"] != null && DBDataset.Tables["Movies"].Rows.Count > 0)
{
ReturnedResults = DBDataSet.Tables["Movies"].Select("Title like '%" + Search + "%'");
}

Related

How to count and compare number in datagirdview c#

I have labels "Hardwork" and 1 datagirdview display when load form. I use the code below to do the quantity comparison in the column "TotalTime". I want if the value is in column "TotalTime"> = 30 then labels "Harwork" + 1
but not run.the result is: specified cast is not valid.
Please, help me fix it
public void BtnSearch_Click(object sender, EventArgs e)
{
db = new DbConnector();
lbTotal.Text = "00";
db.fillDataGridView("select *from tbl_WorkLoad where TimeComplete Between'" + dateTimePicker1.Value.ToString("dd-MM-yy| HH:mm:tt") + "' and '" + dateTimePicker2.Value.ToString("dd-MM-yy| HH:mm:tt") + "'", dataGridView1);
const string HardWorkLabelText = "Hard Work Count: {0}";
const int HardWorkThreshold = 30;
try
{
IEnumerable<DataGridViewRow> rows = dataGridView1.Rows.Cast<DataGridViewRow>().Where(r => ((Int32)r.Cells["TotalTime"].Value) >= HardWorkThreshold);
lbHardwork.Text = string.Format(HardWorkLabelText, rows.Count());
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
lbTotaltime.Text = (Convert.ToString(double.Parse(lbTotaltime.Text) + double.Parse(dataGridView1.Rows[i].Cells[7].Value.ToString())));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
You have apart from the exception, you have few other issues in the code. But let us focus on the issue which you reported.
From the code, I understand that, you want to display the number of hardwork and also you want to display the total hardwork time.
Since you are looping thru the rows of gridview, you can calculate both of these in the for loop.
var hardworkCount = 0;
var totalTimeSum = 0.0;
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
double totalTime = double.Parse(dataGridView1.Rows[i].Cells[7].Value.ToString());
if(totalTime >= HardWorkThreshold)
{
hardworkCount++;
}
totalTimeSum += totalTime;
}
lbHardwork.Text = hardworkCount.ToString();
lbTotaltime.Text = totalTimeSum.ToString();
You life would be a lot simpler if you stop pulling your data out of the datagridview, and just have your datagridview based off a datatable that holds the data. It might be this way already, but we can't see the definition of your filldatagridview method
Here's how I'd do it:
//assuming sql sevrer, use correct dataadapter for your db
using(var da = new SqlDataAdapter "select * from tbl_WorkLoad where TimeComplete Between #a and #b", "YOUR CONNECTION STRING HERE")){
da.SelectedCommand.Parameters.AddWithValue("#a", dateTimePicker1.Value);
da.SelectedCommand.Parameters.AddWithValue("#b", dateTimePicker2.Value);
var dt = new DataTable();
da.Fill(dt);
dataGridViewX.DataSource = dt;
lbHardwork.Text = dt.Compute("Count([TotalTime])", "[TotalTime] >= 30").ToString();
lbTotalTime.Text = dt.Compute("Sum([TotalTime])", "[TotalTime] >= 30").ToString();
}
The way you wrote your SQL is a pattern that risks SQL injection if used with strings - always use parameters in SQL. Always. There is never a reason not to when dealing with data values. See why, and also here
You should read this blog before using AddWithValue with strings in SQL Server. There are better ways to do parameters than AWV, but at the very least it would be preferable to use AddWithValue than what you're doing now
Don't access data in a gridview via the grid; bind the grid to a datatable and if you want data, access the datatable. If you want to get the datatable out of a grid at any point, you can use var dt = datagridviewX.DataSource as DataTable

Get OleDbCommandBuilder 's generated SQL commands

I use OleDbDataAdapter and OleDbCommandBuilder to fill DataSet object with database contents, and then update database according to a changes that I made in the DataSet. The problem is that I get the exception: "Concurrency violation: the UpdateCommand affected 0 of the expected 1 records". I've found an explanation of this error:
Because a record could have been modified after it was returned from
the SELECT statement, but before the UPDATE or DELETE statement is
issued, the automatically generated UPDATE or DELETE statement
contains a WHERE clause, specifying that a row is only updated if it
contains all original values and has not been deleted from the data
source. Where an automatically generated update attempts to update a
row that has been deleted or that does not contain the original values
found in the DataSet, the command does not affect any records, and a
DBConcurrencyException is thrown.
That means that auto generated UPDATE command affected 0 rows in the database. I work with paradox(db-file) database and no one changes it except for me. I guess that my program changes the same row two times somewhere. I wanted to debug my program by executing all generated queries manually and finding which one doesn't affect any row(because actually I'm pretty sure that all changes are made only once and the bug is somewhere else))). Is it possible to run auto generated commands manually?
My code is too big and complicated to post it here but generally it works like this(I made a working project and took it from there)
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.OleDb;
namespace OleDBCommandBuilder
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string cs = #"Provider=Microsoft.Jet.OLEDB.4.0;";
cs += #"Data Source=C:\FOLDER\1\SPR_KMZ\;";
cs += #"Extended Properties=Paradox 5.x;";
OleDbConnection Connection = new OleDbConnection();
Connection.ConnectionString = cs;
try
{ Connection.Open(); }
catch (Exception ex)
{ MessageBox.Show("Error openning database! " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); }
string SQLQuery = "SELECT * FROM SPR_KMZ WHERE REZ<>0";
DataSet SPR_KMZ = new DataSet();
OleDbDataAdapter DataAdapter = new OleDbDataAdapter();
DataAdapter.SelectCommand = new OleDbCommand(SQLQuery, Connection);
OleDbCommandBuilder builder = new OleDbCommandBuilder(DataAdapter);
try
{
DataAdapter.Fill(SPR_KMZ);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(String.Format("Error \n{0}\n{1}", ex.Message, SQLQuery));
Environment.Exit(0);
}
DataRow[] SPR_KMZ_rows = SPR_KMZ.Tables[0].Select("Fkmz=10000912 AND REZ=1");
foreach (DataRow SPR_KMZ_row in SPR_KMZ_rows)
{
SPR_KMZ_row["DN"] = Convert.ToDateTime("30.12.1899");//26.12.2008
SPR_KMZ_row["Price"] = Convert.ToDouble(0);//168,92
}
DataAdapter.Update(SPR_KMZ);
System.Windows.Forms.MessageBox.Show("Success!");
Environment.Exit(0);
}
}
}
P.S. Previously it updated the database without concurrency exception, but after a lot of changes(I commented out the line "DataAdapter.Update(SPR_KMZ);" for a long time for debugging reason, so I don't know when exactly this error started to throw)
P.S.S. there are no INSERTs or DELETEs in my code, only UPDATEs...
<<UPDATE>>
I've found what was the problem: if "DN" field has NULL value then after changing it, the auto-generated UPDATE Statement don't affect anything, obviously because "DN" is contained in a primary key and command builder didn't expect for primary key field to have NULL values(who ever would))), no surprise this engine is called "Paradox")))
that's why in
CommandBuilder.GetUpdateCommand().CommandText
in where clause for "DN" field there was this kind of pattern:
... WHERE ((REZ = ?) AND (DN = ?) AND ...
while nullable fields are described like this:
... AND ((? = 1 AND Price IS NULL) OR (Price = ?)) AND ((? = 1 AND Nmed IS NULL) OR (Nmed = ?)) AND ...
P.S.S.S. Hey, I can try to set UpdateCommand manually to fix this!)))
Here is how I've managed to set the UpdateCommand manually and even get SQL code for every UPDATE command that is being executed!(more or less)). It is very helpful while debugging - I can see what sql query failed to execute during DataAdapter.Update(DBDataSet) command.
public void Update(DataSet DBDataSet)
{
DataAdapter.RowUpdating += before_update;
DataAdapter.Update(DBDataSet);
}
public void before_update(object sender, EventArgs e)
{
//Convert EventArgs to OleDbRowUpdatingEventArgs to be able to use OleDbCommand property
System.Data.OleDb.OleDbRowUpdatingEventArgs oledb_e = (System.Data.OleDb.OleDbRowUpdatingEventArgs) e;
//Get query template
string cmd_txt = oledb_e.Command.CommandText;
//Modify query template here to fix it
//cmd_txt = cmd_txt.Replace("table_name", "\"table_name\"");
//fill tamplate with values
string cmd_txt_filled = cmd_txt;
foreach(System.Data.OleDb.OleDbParameter par in oledb_e.Command.Parameters)
{
string par_type = par.DbType.ToString();
string string_to_replace_with = "";
if (par.Value.GetType().Name == "DBNull")
{
string_to_replace_with = "NULL";
}
else
{
if (par_type == "Int32")
{
par.Size = 4;
string_to_replace_with=Convert.ToInt32(par.Value).ToString();
}
else if (par_type == "Double")
{
par.Size = 8;
string_to_replace_with=Convert.ToDouble(par.Value).ToString().Replace(",",".");
}
else if (par_type == "DateTime")
{
par.Size = 8;
/* In Paradox SQL queries you can't just specify the date as a string,
* it will result in incompatible types, you have to count the days
* between 30.12.1899 and the required date and specify that number
*/
string_to_replace_with = DateToParadoxDays(Convert.ToDateTime(par.Value).ToString("dd.MM.yyyy"));
}
else if (par_type == "String")
{
string_to_replace_with = '"' + Convert.ToString(par.Value) + '"';
}
else
{
//Break execution if the field has a type that is not handled here
System.Diagnostics.Debugger.Break();
}
}
cmd_txt_filled = ReplaceFirst(cmd_txt_filled, "?", string_to_replace_with);
}
cmd_txt_filled = cmd_txt_filled.Replace("= NULL", "IS NULL");
//Get query text here to test it in Database Manager
//System.Diagnostics.Debug.WriteLine(cmd_txt_filled);
//Uncomment this to apply modified query template
//oledb_e.Command.CommandText = cmd_txt;
//Uncomment this to simply run the prepared update command
//oledb_e.Command.CommandText = cmd_txt_filled;
}
public string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
private static string DateToParadoxDays(string date)
{
return (Convert.ToDateTime(date) - Convert.ToDateTime("30.12.1899")).TotalDays.ToString();
}

Populating one dataset overwrites another dataset result

Its been 2-3 hours and I can't still understand why its happening. Need help.
I am populating dataGridView using dataset. There are two tables master and detail. I am using two datasets to fetch both tables from datasets (Also I've implemented class to fetch from database so I just call that class's function which return dataset containing result and I assign that to my local datasets).
Now what is happening is 1st I fetch detail table and check whether its null etc and if not I fetch second. Here the problem arises as soon second gets populated 1st one overwrites. I am so out of my mind now that I can't understand what is happening. here's my code:
private void txtInvNumber_TextChanged(object sender, EventArgs e)
{
if (txtInvNumber.Text != "")
{
try
{
DataSet dsdetail = new DataSet();
DataTable dtdetail = new DataTable();
string query = "SELECT sell.ItemId 'Item ID',Item 'Item Name' ,sell.Quantity ,Status ,Credit_Limit 'Credit Limit',"
+ "Total_amount 'Total Amount' ,Discount_Allowed 'Discount' ,Discounted_amount 'Discounted Amount' , "
+ "Payable_Amount 'Payable Amount',Advance 'Advance Paid',Amount_Received 'Amount Received', Amount_Receivable 'Amount Receivable' "
+ "FROM dbo.DSelling_Information sell join Item_Infomation it on sell.ItemId=it.Item_ID "
+ "where InvoiceNum = '" + txtInvNumber.Text + "'";
string query_master = "select Sale_Orde_date 'Date', mp.Cust_ID,CUST_Name,Total_Quantity,Total_Amount,"
+ "Amount_Received,Amount_Receivable,Report_Status from dbo.MSelling_Information mp join dbo.Customer_Information cs on mp.Cust_ID=cs.CUST_ID"
+ " where Invoice_num = '" + txtInvNumber.Text + "'";
dsdetail = db.func_ds(query);
if (dsdetail != null && dsdetail.Tables != null && dsdetail.Tables[0].Rows.Count > 0)
{
ds2 = db.func_ds(query_master); <--here dsitem gets populated too.
if (ds2 != null && ds2.Tables != null && ds2.Tables[0].Rows.Count > 0)
{
dtSellDate.Value = DateTime.ParseExact(ds2.Tables[0].Rows[0]["Date"].ToString(), "M/dd/yyyy", CultureInfo.InvariantCulture);
txtCustID.Text = ds2.Tables[0].Rows[0]["Cust_ID"].ToString();
txtCustomerName.Text = ds2.Tables[0].Rows[0]["CUST_Name"].ToString();
txtTotalQuantity.Text = ds2.Tables[0].Rows[0]["Total_Quantity"].ToString();
txtTotalAmount.Text = ds2.Tables[0].Rows[0]["Total_Amount"].ToString();
txtAmountRcvd.Text = ds2.Tables[0].Rows[0]["Amount_Received"].ToString();
txtAmountRcvble.Text = ds2.Tables[0].Rows[0]["Amount_Receivable"].ToString();
txtReportStatus.Text = ds2.Tables[0].Rows[0]["Report_Status"].ToString();
dataGridView1.AutoGenerateColumns = true;
dtdetail = dsdetail.Tables[0];
//dataGridView1.DataSource = db.func_ds(query).Tables[0]; // dataset
dataGridView1.DataSource = dtdetail;
isDgFill = true;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
ds1.Clear();
dataGridView1.DataSource = null;
dataGridView1.Columns.Clear();
isDgFill = false;
}
}
I tried using new keyword but no luck
dataGridView1.DataSource = new DataTable("dtdetail");
also this:
dataGridView1.DataSource = new DataSet("dsdetail").Tables[0];
Here is another problem:
I tried direct function call on populating data:
dataGridView1.DataSource = db.func_ds(query).Tables[0];
it worked but on cellclick i need to do multiple calculations which includes fetching data from database so whenever i fetch using dataset it just make datasource of datagridview null.
I am using different names for each dataset but nothing is happening. Please if anyone can tell where I am wrong i'll be thankful to him\her a lot.
If you reassigning data source of data grid view It will clear the previous one .I think this the mistake your making.

Pulling a SELECT query into a datatable and accessing it

I'm writing a small ASP.net C# web page and it keeps giving me an error stating:
There is no row at position 0.
I'm probably doing it wrong but here is some of my code:
string SqlQuery = "SELECT * ";
SqlQuery += " FROM main_list";
SqlQuery += " WHERE ID = #FindID";
SqlConnection conn = new SqlConnection("server=???;database=contacts;User
ID=???;Password=???;");
conn.Open();
SqlCommand SqlCmd = new SqlCommand(SqlQuery, conn);
SqlCmd.Parameters.Add("#FindID",searchID);
SqlDataAdapter da = new SqlDataAdapter(SqlCmd);
try {
da.Fill(dt);
fillData(p);
}
catch {
txtId.Text = "ERROR";
}
And FillData is the following:
protected void fillData(int pos) {
txtId.Text = dt.Rows[pos]["ID"].ToString();
txtCompany.Text = dt.Rows[pos]["Company"].ToString();
txtFirstName.Text = dt.Rows[pos]["First_Name"].ToString();
txtLastName.Text = dt.Rows[pos]["Last_Name"].ToString();
txtAddress1.Text = dt.Rows[pos]["Address1"].ToString();
txtAddress2.Text = dt.Rows[pos]["Address2"].ToString();
txtCity.Text = dt.Rows[pos]["City"].ToString();
txtState.Text = dt.Rows[pos]["State"].ToString();
txtZipCode.Text = dt.Rows[pos]["ZipCode"].ToString();
txtPhoneNum1.Text = dt.Rows[pos]["Phone_Num"].ToString();
txtPhoneNum2.Text = dt.Rows[pos]["Phone_Num2"].ToString();
txtFax.Text = dt.Rows[pos]["Fax_Num"].ToString();
txtEmail.Text = dt.Rows[pos]["Email"].ToString();
txtNotes.Text = dt.Rows[pos]["Notes"].ToString();
txtCategory.Text = dt.Rows[pos]["Category"].ToString();
txtSubCategory.Text = dt.Rows[pos]["SubCategory"].ToString();
txtDateAdded.Text = dt.Rows[pos]["DateAdded"].ToString();
txtDateModified.Text = dt.Rows[0]["DateModified"].ToString();
}
Here is the call that errors out:
protected void btnPrev_Click(object sender, EventArgs e) {
p--;
lblPage.Text = p.ToString();
fillData(p-1);
}
protected void btnNext_Click(object sender, EventArgs e) {
p++;
lblPage.Text = p.ToString();
fillData(p-1);
}
I'm trying to cycle thru the Rows[0] to Rows[1] or however many there is but it gives me the error about no row at position 0 or position 1. It only fills once and then errors out.
EDIT:
I'm trying to access the second row returned by the database after already accessing one row already. For example: Rows[0] is accessible fine but then when I try to read Rows[1] it errors and says it doesn't have a row in position 1. I can revise the code to return Rows[1] and it works but when I try to access Rows[0] it breaks. This is why I pass the variable (p) to fillData so it can show only that Rows value. Thanks!
EDIT 2: I believe it's because there is a postback that wipes the values retrieved by the database. Is there a way to get the database entries to stay even after a postback? If not I am guessing I will have to query the database every time.
The error message indicates there are no rows being returned by SQL. Are you sure there is data to be returned.
When you use dt.Rows[0] you are effectively saying "take the first row that comes back, and get a value from it." If the DataTable doesn't have any rows (i.e. your SQL query returns no matches), that's like saying "Here is a plate that contains no apples. Take the first apple and tell me what colour it is" - see? Doesn't make sense.
What you should do is check whether there are any rows before you try to read them...
if(dt.Rows.Count > 0)
{
// do stuff here.
}
Use Linq and a stored procedure it is much nicer
datacontext context = new datacontext();
var result = context.MyStoredProc(searchID).FirstOrDefault();
Try changing
SqlCmd.Parameters.Add("#FindID",searchID);
to
SqlCmd.Parameters.AddWithValue("#FindID",searchID);
Check your query on your database, make sure rows are actually being returned. Also, it's bad practice to put your query directly into your code like that, especially when using parameters. You might want to try something like this:
private Int32 CallStoredProcedure(Int32 FindId)
{
using (var dt = new DataTable())
{
using (var conn = new SqlConnection(ConnectionString))
{
using (var sqlCmd = new SqlCommand("SEL_StoredProcedure", conn))
{
using (var sda = new SqlDataAdapter(sqlCmd))
{
sqlCmd.CommandType = System.Data.CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#FindId", FindId);
sqlCmd.Connection.Open();
sda.Fill(dt);
}
}
}
if (dt.Rows.Count == 1)
return Convert.ToInt32(dt.Rows[0]["ID"]);
else if (dt.Rows.Count > 1)
throw new Exception("Multiple records were found with supplied ID; ID = " + studentId.ToString());
}
return 0;
}
To set up your stored procedure, on your database run this:
CREATE procedure [dbo].[SEL_StoredProcedure]
#FindId int = null
as
SELECT * FROM main_list where ID = #FindId
Just remove the index identifier from the code:
e.g.
txtId.Text = dt.Rows["ID"].ToString();

Why am I getting a NullReferenceException on a DataSet?

I'm opening up a database query from the Northwind database for each item that my ShoppingCart entails. It is to take ProductID and UnitsInStock out from the Products table. After I take the two columns out from the database to save the data into a DataTabel ds. Then I compare to make sure the quantity user entered is less than the column units in stock in the database.
theCart.Values is part of ICollections.
I am gettign error: from my exception message: "There was a problem connecting to the database: Object reference not set to an instance of an object."
Here's the code.
DataSet ds = new DataSet();
OleDbConnection conn = new OleDbConnection((string)Application["DBConnectionString"]);
foreach (OrderItem item in theCart.Values)
{
string selectionString =
"SELECT Products.ProductID, Products.UnitsInStock " +
"FROM Products" +
"WHERE Products.ProductID = " + item.ProductID + ";";
try
{
OleDbCommand cm = new OleDbCommand(selectionString, conn);
OleDbDataAdapter da = new OleDbDataAdapter();
da.SelectCommand = cm;
da.Fill(ds);
da.Dispose();
if (ds.Tables["Products"].Columns.Count != 0 &&
ds.Tables["Products"].Rows.Count != 0)
{
for (int index = 0; index < ds.Tables["Products"].Rows.Count; index++)
{
if (item.ProductID == int.Parse(ds.Tables["Products"].Rows[index][indexOfProductID].ToString()))
{
if (item.QuantityOrdered > int.Parse(ds.Tables["Products"].Rows[index][indexOfUnitsInStock].ToString()))
{
hasStock = false;
int inStock = int.Parse(ds.Tables["Products"].Rows[index][indexOfUnitsInStock].ToString());
txtUnderstockedItems.Text += "Sorry we do not have enough stock of item: " + item.ProductName +
"<br> Currently, " + item.ProductName + " (ID:" + item.ProductID + ") has " + inStock + " in stock.";
}
else
{//can output how many items in stock here.
hasStock = true;
}
}
}
}
catch (Exception ex)
{
txtUnderstockedItems.Text = "There was a problem connecting to the database: " + ex.Message;
}
finally
{
conn.Close();
}
}
}
Rows or Columns is most likely null. Inspect ds prior to that if statement. A common reason something like this would happen is that the Products table returned nothing. You can't get the property of an object that does not exist, thus the exception. You should do a != null comparison instead of checking the count. If the length is zero the code inside the loop will never execute but you won't crash or anything.
if (ds.Tables["Products"].Columns != null && ds.Tables["Products"].Rows != null)
Just a heads up, this will cause no problems if your row count is zero, but you may need some logic within the loop to check that the columns you're planning to access exist.
You are trying to get the table from the dataset with its hardcoded name as to what you see in the database, I just ran a test on a similar pattern and it looks like when you create a Dataset and fill it from the database as you are doing, the table name is not copied from the Database. As Tim suggested, you should check for ds.Table[0]
DataTableCollection.Item returns null if there's no table with the specified table-name.
If a command does not return any rows, no tables are added to the DataSet, and no exception is raised. So i assume that there's no table in the DataSet because no rows are returned.
I would initialize a single DatatTable manually instead and use the overload of Fill which takes a DataTable.
Dim table = new DataTable("Products")
da.Fill(table)

Categories