Why am I getting a NullReferenceException on a DataSet? - c#

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)

Related

C#: Deleting row in DataTable does not apply to data bank

My task should be quite simple but after hours and hours I must admit I'm completely stuck!
I simply want to delete a datarow from a datatable. My datatable is a copy of the table in my current dataset in a SQLite databank. It is mandatory to use the table.row.Delete() method. I am aware that delete() just marks the row to be deleted upon table update.
Below is the code I'm currently using:
I retrieve my data via:
public DataTable GetTable(string tableName)
{
string connectionPath = dbVariables.ConnectionString;
try
{
SQLiteConnection myConnection = new SQLiteConnection(connectionPath);
myConnection.Open();
string cmdStr = "SELECT * FROM " + tableName;
DataTable myTable = new DataTable();
SQLiteDataAdapter myAdapter = new SQLiteDataAdapter(cmdStr, myConnection);
myAdapter.FillSchema(myTable, SchemaType.Source);
myTable.Columns[dbVariables.ClassesID].AutoIncrement = true;
myTable.Columns[dbVariables.ClassesID].AutoIncrementSeed = 1;
myTable.Columns[dbVariables.ClassesID].AutoIncrementStep = 1;
myAdapter.Fill(myTable);
myConnection.Close();
return myTable;
}
catch (SQLiteException e)
{
MessageBox.Show(e.ToString());
return null;
}
}
Here I manipulate my data:
if (myResult == DialogResult.Yes)
{
//killTable.AcceptChanges();
DataRow[] dr = killTable.Select("" + cmVariables.ClassName + " = '" + cmbClasses.Text + "'");
//First I need to evaluate the row index of the row I want to delete
string indexName = (killTable.Rows.IndexOf(dr[0])).ToString();
int i = Int32.Parse(indexName);
// And we are done - I got my row index
DataRow modifiedRow = killTable.Rows[i];
killTable.Rows[i].Delete();
//I inserted this messagebox just to see the rowstatus - and yes, it is marked as deleted on runtime...
MessageBox.Show(killTable.Rows[i].RowState);
// I refer to this in the text below
killTable.AcceptChanges();
killClass_Execution(killTable, cmbClasses.Text, ShortClassNm);
}
And at least the code to update my datatable back to the databank:
public void UpdateTable(string tableName, DataTable sourceTable, bool newOrEdit)
{
try
{
string connectionPath = dbVariables.ConnectionString;
//Connection erstellen --> der connectString gibt dabei den Pfad an.
SQLiteConnection myConnection = new SQLiteConnection(connectionPath);
myConnection.Open();
//Einen Befehls-String erstellen, der das UPDATE-Command auslöst
// UPDATE cm_ClassTest SET className = userEditInput WHERE className = 'oldClassName'
string myUpdateString = "SELECT * FROM " + tableName + "";
SQLiteDataAdapter myAdapter = new SQLiteDataAdapter(myUpdateString, myConnection);
SQLiteCommandBuilder comBuild = new SQLiteCommandBuilder(myAdapter);
myAdapter.DeleteCommand = comBuild.GetDeleteCommand(true);
myAdapter.UpdateCommand = comBuild.GetUpdateCommand(true);
myAdapter.InsertCommand = comBuild.GetInsertCommand(true);
myAdapter.Update(sourceTable);
myConnection.Close();
if (newOrEdit == true)
{
MessageBox.Show("Klasse erstellt!");
}
else
{
MessageBox.Show("Klasse aktualisiert!");
}
}
catch (SQLiteException e)
{
MessageBox.Show(e.ToString());
}
}
In the code block for manipulating data you will find the AcceptChanges() method. At this time, there may have been no other changes appeared to my datatable - so after app start, deleting a row may be the users first action.
Also: each entry in my dataset is unique (school classes that are labeled with a unique class name).
Any help will be highly appreciated!
Regards,
Aran
OK - so I succeeded (finally). Debugging showed no issues, except for AcceptChanges() which got a call but did not result in actually deleting the dataRow.
All I can think of right now is that my primary approach to it was part of the problem: I get my DataTable right from the DataBase, do all the manipulations with it, and then send it back to DB via Update (see my update method above). The SQLite CommandBuilder seems not capable of interpreting my DAtaRow which is by then flagged 'Deleted', but kind of rubber-stamps it to the end of the method without throwing an exception.
By doing so I tried the GetChanges-Method:
DataRow[] dr = killTable.Select("" + cmVariables.ClassName + " = '" + cmbClasses.Text + "'");
string indexName = (killTable.Rows.IndexOf(dr[0])).ToString();
int i = Int32.Parse(indexName);
killTable.Rows[i].Delete();
DataTable killItTable = killTable.GetChanges(DataRowState.Deleted);
killClass_Execution(killItTable, cmbClasses.Text, ShortClassNm);
Now it's working - the copy of killTable (killItTable - stupid I know, but first thing I do tomorrow morning is give it better names :) ) provides info for the sql commandbuilder which seems to be recognized by it.
Whatever it is - it now works.
I think this is not supposed to happen - if someone can come up with any good suggestions, on how to improve my approach, I shall be glad.
best regards

Duplicate values of every data row

There are 2 rows in my table and I'm receiving values within my site as follows:
I should only be receiving 2 rows so I'm not sure what I've done wrong with my code?
if (binForm.Rows.Count != 0)
{
int rowCounter = binForm.Rows.Count;
int increment = 0;
while (rowCounter > 0)
{
tableData.Append("<tr><td>" + binForm.Rows[increment]["binType"].ToString() + "</td><td>" + binForm.Rows[increment]["binColour"].ToString() + "</td><td>" + binForm.Rows[increment]["date"].ToString() + "</td><tr>");
increment++;
rowCounter--;
}
}
This is how the form is generated:
DataTable binForm = new DataTable();
MySqlDataAdapter dataAdapter = new MySqlDataAdapter("SELECT bin.binType, bin.binColour, missedbin.date FROM bin INNER JOIN missedbin ON missedbin.address_addressID=bin.address_addressID WHERE '" + sessionVarAddress.ToString() + "' = bin.address_addressID ", connect);
dataAdapter.Fill(binForm);
here is the actual data in the bin table.
and missedbin table.
EDIT: It seems as though as suggested my sql query is incorrect as it is returning 4 rows.
The problem will be the join, not this snip-it of code.
if you join on a field with 2 entries the same, it will double up as per this example. please go back and check your query.

C# confirm table row has been deleted from database

I've written a method which will try and delete a row from a db table based on a primary key id. The problem i have is that the try block is always returning "Success" even if a record has already been deleted / or it doesn't exist.
public string delete_visit(int id)
{
string deleteResponse = null;
string cnn = ConfigurationManager.ConnectionStrings[connname].ConnectionString;
using (SqlConnection connection = new SqlConnection(cnn))
{
string SQL = string.Empty;
SQL = "DELETE FROM [" + dbname + "].[dbo].[" + tbname + "] WHERE VisitorNumber = #IDNumber ";
using (SqlCommand command = new SqlCommand(SQL, connection))
{
command.Parameters.Add("#IDNumber", SqlDbType.Int);
command.Parameters["#IDNumber"].Value = id;
try
{
connection.Open();
command.ExecuteNonQuery();
deleteResponse = "Success";
}
catch (Exception ex)
{
deleteResponse = "There was a problem deleting the visit from the database. Error message: " + ex.Message;
}
}
}
return deleteResponse;
}
I want to be able to tell if the row was affected. I can do this in SQL Server Management Studio like so:
DELETE FROM Visits
WHERE VisitorNumber=88;
IF ##ROWCOUNT = 0
PRINT 'Warning: No rows were updated';
So i want to know how do i plug in the ##ROWCOUNT bit into my c# so that i can tell if the row was deleted?
thanks
ExecuteNonQuery() returns an int, indicating how many rows were affected.
So:
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected == 0)
{
deleteResponse = "No rows affected";
}
The problem is that this number can be influenced based on what the query actually does. Executing triggers or calling stored procedures could mess with the output, changing the affected number of rows. If you really must, then first execute a query where you check that the record with the given ID exists.

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.

NullReferenceException error when searching database table for entries

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 + "%'");
}

Categories