我是 EF 的新手,正在嘗試獲取與 Prism MVVM一起運行的 EF Core Getting Started with WPF教程。
我目前堅持使用一個丑陋的解決方案,以在按下“保存”按鈕后將插入的專案的自動遞增 id (sqlite) 反映回 DataGrid。更新:我后來發現這樣做后所有的排序和過濾都會丟失。
在非 mvvm 教程中,這是通過呼叫productsDataGrid.Items.Refresh(). 這很好用:
private void Button_Click(object sender, RoutedEventArgs e)
{
_context.SaveChanges();
productsDataGrid.Items.Refresh();
}
目前對我有用的唯一解決方案(更新:請參閱下文以獲得更好的解決方案。)是將 ObservableCollection 設定為 null,然后在呼叫context.SaveChanges()我的Save()函式后將其重新分配給資料庫背景關系。
這是作業代碼(丟棄過濾和排序):
主視窗.xaml
<Window x:Class="EfTestPrism.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EfTestPrism"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindowViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<CollectionViewSource x:Key="CategoryViewSource"
Source="{Binding CategoriesCollection}"/>
</Window.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding WindowCloseCommand, Mode=OneTime}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
AutoGenerateColumns="False"
RowDetailsVisibilityMode="VisibleWhenSelected"
ItemsSource="{Binding
Source={StaticResource CategoryViewSource}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Category Id"
Width="SizeToHeader"
IsReadOnly="True"
Binding="{Binding CategoryId}"/>
<DataGridTextColumn Header="Name"
Width="*"
Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1"
Content="Save"
Command="{Binding SaveCommand}"/>
</Grid>
</Window>
主視窗.xaml.cs:
namespace EfTestPrism;
public partial class MainWindow
{
public MainWindow() {
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
主視窗視圖模型.cs
using System.Collections.ObjectModel;
using System.Windows.Input;
using Microsoft.EntityFrameworkCore;
using Prism.Commands;
using Prism.Mvvm;
namespace EfTestPrism;
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel() {
context.Database.EnsureCreated();
context.Categories!.Load();
CategoriesCollection = context.Categories!.Local.ToObservableCollection();
}
private readonly ProductContext context = new ();
private ObservableCollection<Category> ? categoriesCollection;
public ObservableCollection<Category> ? CategoriesCollection {
get => categoriesCollection!;
set => SetProperty(ref categoriesCollection, value);
}
public ICommand SaveCommand => new DelegateCommand(Save);
private void Save() {
context.SaveChanges();
/* I don't like the following but it works.
I tried different things here, see below. */
CategoriesCollection = null;
CategoriesCollection = context.Categories!.Local.ToObservableCollection();
}
public ICommand WindowCloseCommand => new DelegateCommand(WindowClose);
private void WindowClose() {
context.Dispose();
}
}
產品背景關系.cs
using Microsoft.EntityFrameworkCore;
namespace EfTestPrism;
public class ProductContext : DbContext
{
public DbSet<Category> ? Categories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) {
options.UseSqlite("Data Source=products.db");
options.UseLazyLoadingProxies();
}
}
分類.cs
namespace EfTestPrism;
public class Category // I tried making this a BindableBase and...
{
public int CategoryId { get; set; } // ...using SetProperty without success
public string Name { get; set; }
}
我嘗試過但沒有成功的事情:
ViewModel::Save() 函式:
RaisePropertyChanged(nameof(CategoriesCollection)- 重繪 每個集合項和/或 id 屬性:
.
foreach (var item in CategoriesCollection) {
RaisePropertyChanged(nameof(item.CategoryId));
RaisePropertyChanged(nameof(item));
}
- 將 id 設定為零并回傳到原始值。奇怪的事情在這里發生,比如資料網格中除了新添加的專案之外的所有 id 都為零:
.
foreach (var item in oc) {
var temp = item.CategoryId;
item.CategoryId = 0;
item.CategoryId = temp;
}
主視窗.xaml:
UpdateSourceTrigger為 CategoryID 系結嘗試所有s。
I can see that the collection changes. When I remove the IsReadonly="True" on the DataGrids CategoryId column, the value gets updated as soon as I double click it after saving (I don't know if the UI is just updated or it actually syncs with the database).
What would be a proper mvvm way to update the DataGrid similarly to the categoryDataGrid.Items.Refresh(); call after _context.SaveChanges() in the Button_Click function of the tutorial?
Update: Refresh callback from ViewModel to View
The following works and keeps sorting and filtering. I don't mind too much about the code behind because it's strictly view related and I think that's acceptable.
Pro: No manual impementation of removing and adding back the items to the collection i.e. least code that works (if there's not a better solution).
缺點:視圖模型必須呼叫委托。所以它實際上必須預測它所使用的視圖可能想要提供回呼。
對上述代碼的更改:
MainWindow.xaml:向 中添加一個x:Name以DataGrid使其可從后面的代碼訪問:
[...]
<DataGrid Grid.Row="0"
x:Name="CategoriesDataGrid"
AutoGenerateColumns="False"
[...]
將 a 添加delegate到 MainWindowViewModel.cs 并在其中呼叫它Save():
[...]
public delegate void Callback();
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel(Callback ? refreshView = null) {
RefreshView = refreshView;
[...]
private readonly Callback ? RefreshView;
[...]
private void Save() {
context.SaveChanges();
RefreshView?.Invoke();
}
[...]
實作并提供一個RefreshView方法MainWindow.xaml.cs:
namespace EfTestPrism;
public partial class MainWindow
{
public MainWindow() {
InitializeComponent();
DataContext = new MainWindowViewModel(RefreshView);
}
private void RefreshView() {
CategoriesDataGrid.Items.Refresh();
}
}
uj5u.com熱心網友回復:
除非 EF 為您設定記憶體中現有專案的屬性,否則您需要從資料庫加載新 ID 并自己更新專案的CategoryId屬性Category。
執行此操作的最簡單方法是在呼叫 之后在建構式中執行相同的操作SaveChanges(),例如將集合屬性設定為具有來自資料庫的新專案的新集合:
private void Save() {
context.SaveChanges();
CategoriesCollection = new ObservableCollection<Category>(context.Categories);
}
然后你應該得到一個包含新的更新Category專案的新集合。
或者,您可以Clear()添加現有集合中的專案,然后將資料庫中的新專案添加到其中:
private void Save()
{
context.SaveChanges();
CategoriesCollection.Clear();
foreach (var category in context.Categories.ToArray())
CategoriesCollection.Add(category);
}
uj5u.com熱心網友回復:
您想要的是將您的物體 ( Category)映射到視圖模型(派生自BindableBase),并在物體更改時手動更新視圖模型(包括在物體集合更改時更新視圖模型集合)。
這樣,當您更新專案時,資料網格會保持其排序、過濾、滾動位置等。
RaisePropertyChanged(nameof(CategoryId))僅在從類別內部呼叫時才起作用- 它PropertyChanged在其實體上引發事件 - 也就是說,MainWindowViewModel并解釋了為什么資料網格不關心并且不更新其單元格。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/365259.html
上一篇:如何在EF中使用強型別ID?
