使用实体框架核心模型时 WPF 数据网格未更新
Posted
技术标签:
【中文标题】使用实体框架核心模型时 WPF 数据网格未更新【英文标题】:WPF datagrid not updating when using Entity Framework Core Models 【发布时间】:2021-10-17 19:22:29 【问题描述】:我有一个 WPF 窗口来显示培训课程的详细信息,以及数据网格中的参与者列表。
我正在使用 Entity Framework Core,我的训练类模型看起来像这样
public partial class TrainingClass
public TrainingClass()
TrainingAttendees = new HashSet<TrainingAttendee>();
public int ClassId get; set; // PK
public DateTime DateFrom get; set;
public DateTime DateTo get; set;
public string CourseName get; set;
public virtual ICollection<TrainingAttendee> TrainingAttendees get; set;
在我的TrainingClassDetailsViewModel
中,我有一个按钮可以打开一个对话窗口,用户可以在其中输入与会者详细信息并单击“保存”按钮
public void AddAttendee(object parameter)
TrainingAttendee attendee = new TrainingAttendee()
TrainingAttendeeViewModel vm = new TrainingAttendeeViewModel(attendee);
_windowService.ShowDialog<TrainingAttendeeEditor>(vm);
if (vm.SaveClicked)
_trainingClass.TrainingAttendees.Add(attendee);
这会添加新的参与者,因此 EF Core 可以在调用 SaveChanges
时更新数据库,但这不会更新数据网格。
我知道我需要使用ObservableCollection
,它使用INotifyPropertyChanged
。如果我实现以下并改用Attendees.Add(attendee);
,这将更新数据网格,但不会更新数据库。
private ObservableCollection<TrainingAttendee> _attendees;
public ObservableCollection<TrainingAttendee> Attendees
get => _attendees;
set
if (_attendees != value)
_attendees = value;
ApplyPropertyChange();
...
_attendees = new ObservableCollection<TrainingAttendee>(_trainingClass.TrainingAttendees);
我能得到的唯一工作方法是同时使用_trainingClass.TrainingAttendees.Add(attendee);
和Attendees.Add(attendee);
。但是通过管理 2 个列表,我发现这很奇怪。我需要自己的ApplyPropertyChanged
来代替。
我将如何最好地实现这一点?
【问题讨论】:
【参考方案1】:您可以使用ObservableCollection<T>
作为集合导航属性的后备存储(代替当前的HashSet<T>
)。甚至更好的是,EF Core 提供了ObservableHashSet<T>
类。例如
using Microsoft.EntityFrameworkCore.ChangeTracking; // <-- required
public partial class TrainingClass
public TrainingClass()
TrainingAttendees = new ObservableHashSet<TrainingAttendee>(); // <--
//...
public virtual ICollection<TrainingAttendee> TrainingAttendees get; set;
有关详细信息,请参阅官方 EF Core 文档中的 Notification entities 部分和整个 Change Tracking 主题。
此外,如果您使用的是 EF Core 5.0 或更高版本,则可以使用 EF Core Change-tracking proxies,它基本上为您实现了所有 INotifyPropertyChanging
和 INotifyPropertyChanged
功能。但是您需要将 all 设为您的属性 virtual 并且还可以访问 DbContext
并使用 CreateProxy
扩展方法而不是 new
来创建新的实体实例。
【讨论】:
又一次让我了解到 EF 核心部门的有趣发展! 谢谢。添加项目似乎运作良好。但是编辑它们并不会更新数据网格,并且尝试删除时会引发异常:System.InvalidOperationException: 'Collection Remove event must specify item position.'
@smally 因为这一切都来自 MS(EF Core),如果某些东西不起作用。使用的 EFC 版本或您使用它的方式存在错误。确保从提供的链接和答案的结尾阅读整个文档。例如,如果实体类没有实现INPC
接口,编辑当然不会反映在视图中。您需要自己实现或使用前面提到的 EF Core 更改跟踪代理。等等。对于Remove
,我不确定,- EFC 有/有很多错误,但不敢相信他们错过了这样的基本案例。【参考方案2】:
这是一个很好的例子,为什么您不应该直接绑定到自动生成 (EF) 模型类。
您应该绑定到视图模型或包装类,并在您的实体对象和这些对象之间进行转换/映射。
在您的 AddAttendee
方法中,您将像当前所做的那样将实体添加到上下文中,但还将视图模型/包装器添加到数据绑定 ObservableCollection<T>
:
if (vm.SaveClicked)
//1. add to EF context
_trainingClass.TrainingAttendees.Add(attendee);
//2. Add to source data-bound collection
vm.SourceCollection.Add(new Wrapper(attendee));
您将找到一个“包装”模型here 的视图模型类示例。
【讨论】:
以上是关于使用实体框架核心模型时 WPF 数据网格未更新的主要内容,如果未能解决你的问题,请参考以下文章