NullReferenceException from linq query in database project - c#

I am slamming my head against the wall trying to figure out why when I click the button, a NullReferenceException is saying that my dbcontext is null at the linq query!!! I don't understand, as I have the dbcontext get filled when the form loads with a hospital entity (see below):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DisplayPatientsTable
{
public partial class PatientForm : Form
{
public PatientForm()
{
InitializeComponent();
}
//Entity Framework dbcontext. All data passes through this object and the database
private HospitalDatabase.HospitalEntities dbcontext = null;
private void PatientForm_Load(object sender, EventArgs e)
{
RefreshPatients();
}
private void RefreshPatients()
{
//Dispose of old dbcontext, if any
if (dbcontext != null)
{
dbcontext.Dispose();
//create new dbcontext so we can reorder records based on edits
dbcontext = new HospitalDatabase.HospitalEntities();
//use LINQ to order the Addresses table contents by last name, then first name
dbcontext.Patients.OrderBy(Patients => Patients.Pat_Last_Name)
.ThenBy(Patients => Patients.Pat_First_Name)
.Load();
// specify DataSource for PatientsBindingSource
patientBindingSource.DataSource = dbcontext.Patients.Local;
patientBindingSource.MoveFirst(); // go to the first result
textBox1.Clear(); //clear the Find TextBox
}
}
private void pat_First_NameLabel_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
// use LINQ to create a data source that contains only people
// with last names that start with the specified text
// use LINQ to filter contacts with last names that
// start with findTextBox contents
//Entity Framework dbcontext. All data passes through this object and the database
if (dbcontext != null)
{
dbcontext.Dispose();
//create new dbcontext so we can reorder records based on edits
dbcontext = new HospitalDatabase.HospitalEntities();
}
var query = from patient in dbcontext.Patients
where patient.Pat_Last_Name.StartsWith(textBox1.Text)
orderby patient.Pat_Last_Name, patient.Pat_First_Name
select patient;
//display matching contacts
// patientBindingSource.DataSource = query.ToList();
// patientBindingSource.MoveFirst(); //
// don't allow add/delete when contacts are filtered
bindingNavigatorAddNewItem.Enabled = false;
bindingNavigatorDeleteItem.Enabled = false;
}
private void button2_Click(object sender, EventArgs e)
{
// allow add/delete when contacts are not filtered
bindingNavigatorAddNewItem.Enabled = true;
bindingNavigatorDeleteItem.Enabled = true;
RefreshPatients(); //change back to initial unfiltered data
}
private void patientBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
Validate(); // validate input fields
patientBindingSource.EndEdit();
//try to save changes
if (dbcontext == null)
{
MessageBox.Show("FirstName and LastName must contain values");
}
dbcontext.SaveChanges();
RefreshPatients();
}
}
}

You're only running the following line if dbcontext != null... but it's null when your form first loads, so the code inside that if block is never going to execute.
dbcontext = new HospitalDatabase.HospitalEntities();
You'll have to rework your logic. Maybe something as simple as this, where you check the value before disposing of the object, but then run the rest of the code regardless.
//Dispose of old dbcontext, if any
if (dbcontext != null)
dbcontext.Dispose();
//create new dbcontext so we can reorder records based on edits
dbcontext = new HospitalDatabase.HospitalEntities();
Note that I can't comment on whether disposing and creating a new entity like this is a good practice - I'm not familiar enough with the technology. I'll trust that it is.

Related

Deleting list items with SwipeView in Xamarin

I've made a contacts app in Xamarin and wanted to incorporate the SwipeView, but I can't figure out how to delete single contacts from my list. Any help?
using System;
using System.Collections.Generic;
using SQLite;
using System.Linq;
using Contacts.Classes;
using Xamarin.Forms;
using System.Diagnostics;
namespace Contacts
{
public partial class ContactsPage : ContentPage
{
public ContactsPage()
{
InitializeComponent();
Device.SetFlags(new[] {"SwipeView_Experimental"});
}
void NewContactToolbarItem_Clicked(System.Object sender, System.EventArgs e)
{
Navigation.PushAsync(new MainPage());
}
protected override void OnAppearing()
{
base.OnAppearing();
using(SQLiteConnection conn = new SQLiteConnection(App.FilePath))
{
conn.CreateTable<Contact>();
var contacts = conn.Table<Contact>().ToList();
contactsListView.ItemsSource = contacts;
Command DeleteCommand = new Command<Contact>(contact => { conn.Delete(contact); });
}
}
As Jason said, create a database access class include connection, deleting operation and so on. You could check the database access class in the link below: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/data/databases#data-manipulation-methods
Choose the Item you want to delete and invoke the delete method of SQLite database.
conn.DeleteAsync(item);
After deleting the Item, use the code below to get the list of database and reset the ItemSource.
conn.Table<Contact>().ToListAsync();
You could download the source file from the link below:
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/todo/

C# to access Database connection troubles

I am new to C#, and 1st post here. I'm am writing a windows form app to be used as a database entry tool. to do this I'm connecting to an access database to read lists and capture new inputs
the error I'm getting is:
System.InvalidOperationException: 'ExecuteReader: Connection property has not been initialized.'
at
OleDbDataReader reader = cmd.ExecuteReader();
The code of the form that is driving the fault is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.OleDb;
namespace Metrics_Data
{
public partial class Login : Form
{
Welcome conndata = new Welcome();
public Login()
{
InitializeComponent();
}
private void btn_exit_Click(object sender, EventArgs e)
{
this.Close();
}
private void Login_Load(object sender, EventArgs e)
{
}
private void btn_login_Click(object sender, EventArgs e)
{
//Verify Password and UserID
string Password = null;
OleDbCommand cmd = new OleDbCommand("SELECT * from [Users] WHERE UserID = #UserID",conndata.myconn);
cmd.Parameters.Add("#UserID", OleDbType.VarChar).Value = txt_userid;
OleDbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Password = reader["User_Password"].ToString();
}
reader.Close();
cmd.Dispose();
//Load Next Form
UserHome userHomeform = new Metrics_Data.UserHome();
this.Hide();
userHomeform.ShowDialog();
}
}
}
The code that connects from the welcome screen is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.OleDb;
using Microsoft.Win32;
namespace Metrics_Data
{
public partial class Welcome : Form
{
public OleDbConnection myconn = null;
public Welcome()
{
try
{
InitializeComponent();
OleDbConnection myconn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\148.96.211.237\GroupShares\Quality\Electronic Checksheets\MD.accdb;jet OLEDB:Database Password=""Warranty""");
myconn.Open();
lbl_Connstatus.Text = "Connected to DB";
p_dbconn.Visible = true;
}
catch(Exception)
{
MessageBox.Show("error connecting to DB");
btn_cfg.Visible = false;
btn_home.Visible = false;
btn_help.Visible = true;
}
}
class Variables
{
public static dynamic LoginType;
public static dynamic UserID;
public static dynamic Admin;
public static dynamic FirstUse;
public static dynamic Zone;
public static dynamic Shift;
public static dynamic Group;
public static dynamic CDate;
public static dynamic GateLevel;
public static dynamic CountermeasureID;
}
private void Welcome_Load(object sender, EventArgs e)
{
}
private void btn_help_Click(object sender, EventArgs e)
{
//If program is unable to connect with database, initiate sequence where computer's installed access database engine's are displayed in popups, then tell user that program cannot connect
//popups are displayed to help user. If no database engine popups are displayed, see the following URL for troubleshooting information:
//https://support.microsoft.com/en-us/help/2874601/can-t-use-the-access-odbc-driver-or-oledb-provider-outside-office-clic
//A possible fix to this problem is to install access runtime 2013. The URL below has the link to where it's stored locally:
//\\10.35.193.112\Open\AccessRuntime2013
//Update: This also resolves concerns for computers with MS Office 2016, in which the user will receive a popup indicating they have an OLEDB database engine, but the program still will
//not work. IT may be required to give users temporary administrative rights to install AccessRuntime2013.
string AccessDBAsValue = string.Empty;
RegistryKey rkACDBKey = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Classes");
if (rkACDBKey != null)
{
foreach (string subKeyName in rkACDBKey.GetSubKeyNames())
{
if (subKeyName.Contains("Microsoft.ACE.OLEDB"))
{
MessageBox.Show(Convert.ToString(subKeyName));
}
}
}
MessageBox.Show("The program was unable to connect to the database.");
}
private void btn_exit_Click(object sender, EventArgs e)
{
Environment.Exit(1);
}
private void btn_home_Click(object sender, EventArgs e)
{
Login loginform = new Metrics_Data.Login();
Variables.LoginType = "Data Entry";
loginform.ShowDialog();
}
Any ideas of what I am doing wrong here??? I have verified path to database and the connection string at initial connection does not give any errors, nor do I have any errors in the error space on visual studio.
Many Thanks!
As pointed out in the comments, your constructor is hiding the form property myconn with a local variable, so when you reference the form property, it will be null.
However, I'd be remiss if I did not suggest that you NOT store the connection in a form property. ADO.NET Connections are pooled and are generally cheap to create, so just create one when you need one and dispose of it (using using) when you're done with the query it's created for.
You can store the connection string in one place, but sharing connection objects is generally a bad idea.
Not using a shared connection will also make it simpler since you don't have to worry about checking if the connection is open or not, worrying about opening it twice, etc.

The operation cannot be completed because the DbContext has been disposed. Affect running program

The operation cannot be completed because the DbContext has been disposed.
I was wondering if someone help me with this issue. Here is my code:
public partial class CustomerResearchForm : MetroFramework.Forms.MetroForm
{
FactorEntities contex;
public CustomerResearchForm()
{
InitializeComponent();
}
private void CustomerResearchForm_Load(object sender, EventArgs e)
{
}
private void CResearchGrid_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
CustomerUpdateAndDelete CustomerUpdateAndDelete = new CustomerUpdateAndDelete();
using ( contex =new FactorEntities())
{
var sendergrid=(DataGridView)sender;
int customercode = Convert.ToInt32(sendergrid.Rows[e.RowIndex].Cells[1].Value);
var customer = from _customer in contex.tblCustomers where
_customer.CustomerCode==customercode select _customer;
CustomerUpdateAndDelete.tblCustomerBindingSource.DataSource = customer.ToList();
CustomerUpdateAndDelete.Show();
CustomerUpdateAndDelete.tblCustomerBindingNavigatorSaveItem.Click+=tblCustomerBindingNavigatorSaveItem_Click;
}
}
private void tblCustomerBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
contex.SaveChanges();
throw new NotImplementedException();
}
}
The exception occurs in this line:
contex.SaveChanges();
I can not use var for my context so what should I do?
Your using statement will automatically dispose contex at the end of your closing bracket } in CResearchGrid_CellMouseDoubleClick()
I'm not exactly sure what you are saving, but to get by, you should add a using statement and initialize your contex object. If you do go the route of initializing your contex in each method, you should remove it from your class member declarations. Note that you would need to modify your entities or add new entities to your contex object for you to actually save anything. Without that, you're not really saving anything.
Another way to get going is you would initialize contex in your constructor and implement IDisposable. Then you can call contex.Dispose() in the Dispose() method.

Winform BindingNavigator not saving item to database

I have designed a WinForm which is bound to my database using ADO.Net Entity Framework. On load my details form is populated with data from the database.
I can navigate through the items, however I can not add, save or update the item.
Below is the code for my form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
cpdEntities dbcontext;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dbcontext = new cpdEntities();
cpd_recipientsBindingSource.DataSource = dbcontext.cpd_recipients.ToList();
}
private void cpd_recipientsBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
dbcontext = new cpdEntities();
dbcontext.SaveChanges();
}
}
}
Here is a simple example working with EntityFramework 4
How to Load:
using (var con = new cpdEntities())
{
cpd_recipientsBindingSource.DataSource = con.cpd_recipients.ToList();
}
How to Insert and Update:
if (cpd_recipientsBindingSource.Current == null) return;
using (var con = new cpdEntities())
{
var p = new Customer()
{
CustomerId = ((cpd_recipients)cpd_recipientsBindingSource.Current).Id,
CustomerIdNo = IdNoTextBox.Text,
CustomerName = CustomerNameTextBox.Text
};
var cus = new Customer();
if (p.CustomerId > 0)
cus = con.Customers.First(c => c.CustomerId == p.CustomerId);
cus.CustomerId = p.CustomerId;
cus.CustomerIdNo = p.CustomerIdNo;
cus.CustomerName = p.CustomerName;
if (p.CustomerId == 0)
con.Customers.AddObject(cus);
con.SaveChanges();
int i = cus.CustomerId;//SCOPE_IDENTITY
}
}
It looks like your re-instantiating your DbContext class in each of the events. Remove re-insantiating the DbContext from your saveItem event and give it a try again.

How do you correctly update a databound datagridview from a background thread

I have a custom object that implements INotifyPropertyChanged. I have a collection of these objects where the collection is based on BindingList
I have created a binding source for the collection, and set the datasources of the bindingsource and datagridview.
Everything works great, except I need to update properties on the custom object from background threads. when I do so, I get the following error :
BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refere back to BindingSource
I found the following post that seems to have my exact problem (and solution?) but I cannot quite figure it out.
http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/3566f7c7-eb47-422e-ab09-9549a18da360/
I created and initialized the oper variables per the post in my business object, and then I put the two event functions into my collection class. This compiled correctly, but hangs without exception when run.
I have seen many posts saying to use Invoke/Begin Invoke, but I am not calling any functions on the UI, just updating business objects, so I am not sure where I would put the invoke calls.
One restriction : I want the business object to remain unaware of who is displaying it (as there are multiple consumers) so sending in GUI references into the business object so that I am later able to call invoke using those references is not an option.
I found this class in a forum that works. Just use this instead of BindingList
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
namespace Utility
{
public class ThreadedBindingList<T> : BindingList<T>
{
SynchronizationContext ctx = SynchronizationContext.Current;
protected override void OnAddingNew(AddingNewEventArgs e)
{
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
// SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}
}
Since I took the time to format the sample for my needs I might as well post it here as a readable reference. Nothing changed except formatting.
using System.ComponentModel;
using System.Threading;
namespace Utility
{
public class ThreadedBindingList : BindingList
{
SynchronizationContext ctx = SynchronizationContext.Current;
protected override void OnAddingNew(AddingNewEventArgs e)
{
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate { BaseAddingNew(e); }, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
// SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate { BaseListChanged(e); }, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}
}
Not quite thread safe, but this small change to the above answers could have a big impact if your background thread is modifying object properties faster than they can be displayed;
protected override void OnListChanged(ListChangedEventArgs e)
{
// SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseListChanged(e);
}
else if(e.ListChangedType == ListChangedType.ItemChanged)
{
ctx.Post(delegate { BaseListChanged(e); }, null);
}
else
{
ctx.Send(delegate { BaseListChanged(e); }, null);
}
}
Welcome any suggestions for reducing the number of posted calls if the same object has been modified more than once, and making sure any later send call will block until all posted calls have been processed.

Categories