使用实体框架核心模型时 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&lt;T&gt; 作为集合导航属性的后备存储(代替当前的HashSet&lt;T&gt;)。甚至更好的是,EF Core 提供了ObservableHashSet&lt;T&gt; 类。例如

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,它基本上为您实现了所有 INotifyPropertyChangingINotifyPropertyChanged 功能。但是您需要将 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&lt;T&gt;

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 数据网格未更新的主要内容,如果未能解决你的问题,请参考以下文章

在实体框架核心中合并迁移

如何使用实体框架更新数据库中的记录时覆盖模型验证

WPF C# 应用程序在安装更新时更新实体框架

保存已编辑的核心数据实体未保存

如何使用实体框架从 DataGrid 更新数据库

实体框架-关系未更新