I'm not sure about how to correctly use the DbContext for Entities bound to a WPF DataGrid?
How do I correctly "re-attach" and save changes to the database for all the entities that were loaded to the datagrid during UserControl load?
I was using a DbContext as a member variable and ObservableCollection as DataSource for Datagrids. So everything was fine so far, no need to search for errors in the code below. Just to show what I have done so far.
// Old code - working perfectly as desired
private TestMenuDataContext _Db;
public ObservableCollection<Vendor> Vendors { get; set; }
private void ucGeneralSettings_Loaded(object sender, RoutedEventArgs e) {
//Do not load your data at design time.
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) {
_Db = new TestMenuDataContext();
_Db.Database.EnsureCreated();
Vendors = new ObservableCollection<Vendor>(_Db.Vendors);
Vendors.CollectionChanged += Vendors_CollectionChanged;
vendorDataGrid.ItemsSource = Vendors;
}
}
private void Vendors_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
_Db.Vendors.AddRange(e.NewItems.Cast<Vendor>());
foreach (var vendor in e.NewItems.Cast<Vendor>()) {
vendor.TimeStamp = DateTime.Now;
vendor.UserName = Environment.UserName;
}
break;
case NotifyCollectionChangedAction.Remove:
_Db.Vendors.RemoveRange(e.OldItems.Cast<Vendor>());
break;
}
}
private void SaveSettingsButton_Click(object sender, RoutedEventArgs e) {
var queryDeletedUsedVendor = _Db.TestMenu.Where(t => !Vendors.Any(v => v.Name== t.Vendor));
if (queryDeletedUsedVendor.Any()) {
_AppManager.AddStatusMessage($"Saving settings not possible. Vendor {queryDeletedUsedVendor.FirstOrDefault().Vendor} deleted but it is in use in the Test Menu!", State.Error);
return;
}
try {
_Db.SaveChanges();
_AppManager.AddStatusMessage("Settings saved", State.Ok);
}
catch (Exception ex) {
_AppManager.AddStatusMessage($"Saving data failed {ex.Message}", State.Error);
}
// fire delegate event to inform MainWindow
onDatabaseUpdated?.Invoke(this);
}
private void ucGeneralSettings_Unloaded(object sender, RoutedEventArgs e) {
if (_Db != null)
_Db.Dispose();
}
BUT, currently starting with MVVM and search how to correctly integrate EF Core. Now I have read several times:
Your DbContext lifetime should be limited to the transaction you are running.
E.g. here:c# entity framework: correct use of DBContext class inside your repository class
So taking this into account and changed the saving code to:
// new code
using (TestMenuDataContext db = new TestMenuDataContext())
{
foreach (Vendor v in Vendors) {
var test = db.Vendors.Attach(v);
bool isAlreadyInside = db.Vendors.Any(v2 => v2.Id == v.Id);
if (!isAlreadyInside)
db.Vendors.Add(v);
}
db.SaveChanges();
Do I really need to loop over all entities, attach every single entity and check manually for deleted or added entities? I don't like to have a DbContext opened every time when CollectionChanged event appears. I can't believe it should be this complicated... So currently I would prefer to go with the DbContext as member variable as used before...
If I'm googling correct the not implemented disconnected entities aren't intended to be used in a WPF app with DB-Server connection, they are meant to be used in n-tier environment. So this is not the topic to search for, correct?
Do I need disconnected entities?
Disconnected Entities on MSDN