【问题标题】:WPF datagrid not updating when using Entity Framework Core Models使用实体框架核心模型时 WPF 数据网格未更新
【发布时间】: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 来代替。

我将如何最好地实现这一点?

【问题讨论】:

    标签: c# wpf entity-framework xaml entity-framework-core


    【解决方案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 的视图模型类示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-19
      • 1970-01-01
      • 2014-04-19
      相关资源
      最近更新 更多