C# EF Core-一次性DbContext和Attach()-或-DbContext作为成员或断开连接的实体
我不确定如何为绑定到WPF数据网格的实体正确使用DbContext 在用户控件加载期间,如何正确地“重新连接”并将对数据库的更改保存到加载到datagrid的所有实体 我使用DbContext作为成员变量,使用ObservableCollection作为Datagrids的数据源。所以到目前为止一切都很好,不需要在下面的代码中搜索错误。只是为了展示我到目前为止所做的一切C# EF Core-一次性DbContext和Attach()-或-DbContext作为成员或断开连接的实体,c#,.net,wpf,datagrid,entity-framework-core,C#,.net,Wpf,Datagrid,Entity Framework Core,我不确定如何为绑定到WPF数据网格的实体正确使用DbContext 在用户控件加载期间,如何正确地“重新连接”并将对数据库的更改保存到加载到datagrid的所有实体 我使用DbContext作为成员变量,使用ObservableCollection作为Datagrids的数据源。所以到目前为止一切都很好,不需要在下面的代码中搜索错误。只是为了展示我到目前为止所做的一切 // Old code - working perfectly as desired private TestMenuData
// 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();
}
我真的需要在所有实体上循环,附加每个实体并手动检查删除或添加的实体吗?我不希望每次出现CollectionChanged事件时都打开DbContext。我不敢相信会这么复杂。。。因此,目前我更倾向于使用DbContext作为以前使用的成员变量…如果我在谷歌上搜索更正,则未实现的断开连接的实体不打算在具有DB Server连接的WPF应用程序中使用,它们将用于n层环境。所以这不是要搜索的主题,对吗?
我不希望每次出现
CollectionChanged
事件时都打开DbContext
那就不要。在视图模型中创建一个TestMenuDataContext
,并像以前一样使用这个
所以现在我更喜欢使用DbContext
作为成员变量,就像以前一样
没有什么能阻止你这么做,是吗?显然,在本例中,您确实希望视图模型的每个实例都有一个TestMenuDataContext
。只需在视图模型中(例如在其构造函数中)创建一次TestMenuDataContext
,然后在CollectionChanged
事件处理程序中使用此函数。或者在save方法中创建上下文
DbContext
的最佳生存期当然会因您的需求而有所不同。一般来说,您应该使用短期上下文,但在这种情况下,您似乎确实希望(并且应该使用)对数据网格中的实体对象所做的所有更改都使用相同的上下文
当然,另一个选项是在按下“保存”按钮时(并非每次修改内存中的集合时)创建上下文并附加实体。侧注:
在MVVMObservableCollection.CollectionChanged
中,应该通知视图模型中的更改,以及相应的ViewModel。我不建议让视图修改ObservableCollection
,然后使用CollectionChanged
来反映ViewModel中的更改。尝试保持视图模型->视图通知流,而不是相反方向。*每个更改都在视图模型中完成,并反映在视图中
第一种方法:
基本上分离应用程序逻辑和数据访问,这正是viewmodel的用途
public class YourPageViewModel
{
private readonly ObservableCollection<VendorItemVm> _deletedVendors = new ObservableCollection<VendorItemVm>();
public List<VendorItemVm> Vendors { get; } = new List<VendorItemVm>();
void Add()
{
Vendors.Add(new VendorItemVm
{
IsNew = true,
Id = new Guid(),
UserName = "New Vendor",
});
}
void Remove(VendorItemVm vendor)
{
Vendors.Remove(vendor);
_deletedVendors.Add(vendor);
}
async Task Load()
{
using(var db = new DbContext())
{
var vendors = db.Vendors.AsNoTracking().ToList();
foreach(var entity in vendors)
{
Vendors.Add(new VendorItemVm
{
Id = entity.Id,
Name = entity.Name,
});
}
}
}
async Task Save()
{
using (var db = new DbContext())
{
//convert viewmodels to entities
var newVendorsEntities = Vendors
.Where(v => v.IsNew)
.Select(v => new Vendor
{
Id = v.Id,
UserName = v.UserName,
TimeSpan = DateTime.Now,
})
.ToArray();
//add new entities
foreach (var vm in Vendors.Where(v => v.IsNew))
{
var entity = new Vendor
{
Id = vm.Id,
UserName = vm.UserName,
TimeSpan = DateTime.Now,
};
db.Vendors.Add(vendor);
}
//delete removed entities:
foreach(var vm in _deletedVendors)
{
var entity = new Vendor { Id = vm.Id };
db.Vendors.Attach(entity);
db.Ventors.Remove(entity);
db.Vendors.Add(vendor);
}
await db.SaveChangesAsync();
//reset change tracking
foreach (var vm in Vendors) vm.IsNew = false;
_deletedVendors.Clear();
}
}
}
公共类YourPageViewModel
{
私有只读ObservableCollection _deletedVendors=新ObservableCollection();
公共列表供应商{get;}=new List();
void Add()
{
添加(新的VendorItemVm)
{
IsNew=true,
Id=新Guid(),
UserName=“新供应商”,
});
}
作废删除(VendorItemVm供应商)
{
供应商。删除(供应商);
_deletedVendors.Add(供应商);
}
异步任务加载()
{
使用(var db=new DbContext())
{
var vendors=db.vendors.AsNoTracking().ToList();
foreach(供应商中的var实体)
{
添加(新的VendorItemVm)
{
Id=entity.Id,
Name=entity.Name,
});
}
}
}
异步任务保存()
{
使用(var db=new DbContext())
{
//将视图模型转换为实体
var newVendorsEntities=供应商
.其中(v=>v.IsNew)
.选择(v=>新供应商
{
Id=v.Id,
UserName=v.UserName,
TimeSpan=日期时间。现在,
})
.ToArray();
//添加新实体
foreach(Vendors.Where中的var vm(v=>v.IsNew))
{
var实体=新供应商
{
Id=vm.Id,
UserName=vm.UserName,
TimeSpan=日期时间。现在,
};
db.Vendors.Add(供应商);
}
//删除删除的实体:
foreach(在_deletedVendors中的var vm)
{
var entity=newvendor{Id=vm.Id};
db.供应商.附件(实体);
db.Ventors.Remove(实体);
db.Vendors.Add(供应商);
}
等待db.saveChangesSync();
//重置更改跟踪
foreach(供应商中的var vm)vm.IsNew=false;
_deletedVendors.Clear();
}
}
}
第二种方法:
在前面的示例中,我们基本上实现了自己的基本工作单元模式。然而,DbContext已经实现了UoW和变更跟踪模式
我们将创建DBContext的实例,但我们将仅将其用于跟踪添加/删除的实体:
public class YourPageViewModel
{
MyDbContext _myUoW;
public ObservableCollection<Vendor> Vendors { get; } = new ObservableCollection<Vendor>();
void Add()
{
var entity = new Vendor
{
Id = new Guid(),
UserName = "New Vendor",
};
Vendors.Add(entity)
_myUoW.Vendors.Add(entity);
}
void Remove(VendorItemVm vendor)
{
Vendors.Remove(vendor);
_myUoW.Vendors.Attach(entity);
_myUoW.Vendors.Add(entity);
}
async Task Load()
{
using(var db = new MyDbContext())
{
Vendors = db.Vendors.AsNoTracking.ToList();
foreach(var entity in vendors) Vendors.Add(entity);
}
_myUoW = new MyDbContext();
//if you want to track also changes to each vendor entity, use _myUoW to select the entities, so they will be tracked.
//In that case you don't need to attach it to remove
}
async Task Save()
{
//add new entities and delete removed entities
_myUoW.SaveChanges();
//reset change tracking
_myUoW.Dispose();
_myUoW = new MyDbContext();
}
}
公共类YourPageViewModel
{
MyDbContext_myUoW;
公共ObservableCollection供应商{get;}=new ObservableCollection();
void Add()
{
var实体=新供应商
{
Id=新Guid(),
UserName=“新供应商”,
};
供应商。添加(实体)
_myUoW.Vendors.Add(实体);
}
作废删除(VendorItemVm供应商)
{
供应商。删除(供应商);
_myUoW.Vendors.Attach(实体);
_myUoW.Vendors.Add(实体);
}
异步任务加载()
{
我们
public class YourPageViewModel
{
MyDbContext _myUoW;
public ObservableCollection<Vendor> Vendors { get; } = new ObservableCollection<Vendor>();
void Add()
{
var entity = new Vendor
{
Id = new Guid(),
UserName = "New Vendor",
};
Vendors.Add(entity)
_myUoW.Vendors.Add(entity);
}
void Remove(VendorItemVm vendor)
{
Vendors.Remove(vendor);
_myUoW.Vendors.Attach(entity);
_myUoW.Vendors.Add(entity);
}
async Task Load()
{
using(var db = new MyDbContext())
{
Vendors = db.Vendors.AsNoTracking.ToList();
foreach(var entity in vendors) Vendors.Add(entity);
}
_myUoW = new MyDbContext();
//if you want to track also changes to each vendor entity, use _myUoW to select the entities, so they will be tracked.
//In that case you don't need to attach it to remove
}
async Task Save()
{
//add new entities and delete removed entities
_myUoW.SaveChanges();
//reset change tracking
_myUoW.Dispose();
_myUoW = new MyDbContext();
}
}