如何将 MVVM 与 EF 一起使用(SQLite 中有一个非常复杂的数据库)?
How to use MVVM with EF (with a pretty complex database in SQLite)?
我正在尝试拥有 MVVM 架构,而模型也是 EF 模型。
在代码中:
型号:
public class NotaireDBContext : DbContext
{
public DbSet<Paquet> Paquets { get; set; }
public DbSet<Personne> Personnes { get; set; }
public DbSet<Contrat> Contrats { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite(@"Data Source=db/Notaire.db");
}
public class Paquet
{
public int PaquetId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public string Volume { get; set; }
public string Page { get; set; }
public string Etat { get; set; }
public List<Contrat> Contrats { get; } = new List<Contrat>();
}
public class Personne
{
public int PersonneId { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Nom_pere { get; set; }
public PieceIdentite Piece_identite { get; set; }
public string Num_piece { get; set; }
public string Lieu_naissance { get; set; }
public string Date_naissance { get; set; }
public string Commune { get; set; }
public string Numero_acte { get; set; }
public string Laiv_carte { get; set; } //??????????????
public string Adresse { get; set; }
public string Nationalite { get; set; }
public string Fonction { get; set; }
}
public class Contrat
{
public int ContratId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public List<Personne> Partie_1 { get; set; }
public List<Personne> Partie_2 { get; set; }
public int PaquetId { get; set; }
public Paquet Paquet { get; set; }
}
观看次数:
PaquetsView.xaml(这是所有paquets的视图)
<ScrollViewer Background="#EBEEF5" HorizontalScrollBarVisibility="Disabled"
FlowDirection="RightToLeft">
<ItemsControl x:Name="PaquetsControl" Padding="4">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:PaquetControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--<controls:PaquetControl/>
<controls:PaquetControl/>-->
</ItemsControl>
</ScrollViewer>
我在 PaquetsView.xaml.cs 中像这样绑定它的 ItemsSource :
public partial class PaquetsView : UserControl
{
private NotaireDBContext db = new NotaireDBContext();
public PaquetsView()
{
InitializeComponent();
PaquetsControl.ItemsSource = (from p in db.Paquets select p).ToList();
}
}
PaquetView.xaml的DataTemplate -> ItemsControl位于另一个xaml文件中(PaquetControl.xaml),这是一个UserControl,由带有Menu(和菜单项)的TextBlocks和Buttons组成,即显示数据保存在Paquet上,应该可以edit/delete表示Paquet。
其中一部分:
...
<Button x:Name="MoreButton" Style="{DynamicResource MoreButtonTemplate}"
Grid.Column="2" Click="MoreButtonClicked" Margin="0,-4,-4,0">
<Button.ContextMenu>
<ContextMenu Background="White" FlowDirection="RightToLeft">
<MenuItem Header="Edit" Click="EditMenuItemClick"/>
<MenuItem Header="Archive" Click="ArchiveMenuItemClick"/>
<MenuItem Header="حذف" Click="DeleteMenuItemClick"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
...
<TextBlock Grid.Column="0" Text="{Binding Path=Numero}" FontSize="22" Foreground="Black"/>
...
<TextBlock Grid.Row="1" Text="{Binding Path=Date, StringFormat=yyyy/MM/dd}"
Foreground="Black" FontSize="16"/>
...
<!--other TextBlock binded-->
现在我想知道如何通过更新视图使其增删改查。
总而言之,我有一个用于数据持久化的 SQLite 数据库(代码优先),我可以使用 DBContext 获取该数据,但现在我发现最好使用 MVVM 而不是每次都创建 DBContext。
这是一个很大的话题,我怀疑它是否适合这里采用的格式。
因此,我将仅简要概述要点。
通常使用 MVVM 模式实现 WPF。
它是一个严格的 3 层架构:View (WPF) -> ViewModel -> Model。
模型负责处理“真实”数据——这就是所谓的业务逻辑。
View负责创建GUI.
WPF 的特殊之处在于 UI 元素本身通过绑定请求它们需要的数据。
绑定(大部分)创建到 DataContext。
所以,有必要放一些特殊的自定义类型放在那里,负责View和Model之间的链接。
这种类型称为 ViewModel。
在典型的实现中,模型基本上通过方法接收 / returns 数据。
绑定需要属性。
因此,ViewModel 的主要功能之一就是在其属性中提供 View 所需的所有数据。
当应用程序使用数据库时,在 Sharpe 中习惯于在存储库(数据)模式中实现它。
从 MVVM 的角度来看,这样的 Repository 是 Model 的一部分。
但是,为了更简单的理解,为了便于软件维护,Repository通常是在一个单独的层中实现的。
结果,我们得到了一个四层架构:View -> ViewModel -> Model -> Repository。
根据 OOP 的规则和原则,SOLID 在分层架构中,每一层“知道”(具有信息)仅关于底层。
并且所有非public信息都必须封装在层内。
EF 实体反映数据库数据,它们是可变的,可以有相应的属性。
更改源时,这些类型可以更改。
假设您在某个时候想要使用一组 XML 文件而不是数据库。
他们需要不同类型的实体。
因此,此类实体是存储库的内部实现。
要与模型交换数据,存储库必须是模型类型或通用 DTO 类型。
在下一级,ViewModel 还必须从模型接收数据。
但是这里不能使用模型类型,因为它们可以隐式地与业务逻辑相关联,并且有可能创建寄生连接,从而导致不可预测的错误。
在此级别(ViewMode-> Model),专门使用 DTO 类型进行数据交换。
它们最好是不可变的。
View 与 ViewModel 的下一级交换。
首先,GUI 通常需要可变属性。为了自动更新 属性 视图,类型必须实现 INotifyPropertyChanged。
其次,要从 GUI 调用操作,ViewModel 必须在其属性中提供 COMMANDS - 这是 ICommand 实现。
对于我在这里的回答,我使用 .
第三,在View的类型中,通常需要额外的属性来确保GUI操作的逻辑:一个被选择的元素,一个展开的元素,而不是记录的Id,一个反映它的元素等
由于这些原因,在 ViewModel 级别,您已经需要自己的类型和 DTO 以外的实现。
作为上述结果,我们可以得到反映数据库中某些记录的类型的四种不同实现。
每种类型的使用面积都会在一层或两层。
为了不混淆这一切,最好在一个单独的项目中做每一层。
仅在层内使用的类型在此项目中实现。
多个层使用的类型(例如,DTO)在单独的库中实现(可能用于简单任务和一个公共库)。
为了保持抽象,最好通过接口的初步声明来完成所有的实现。
并通过这些接口在层与层之间传递信息。
我正在尝试拥有 MVVM 架构,而模型也是 EF 模型。
在代码中:
型号:
public class NotaireDBContext : DbContext
{
public DbSet<Paquet> Paquets { get; set; }
public DbSet<Personne> Personnes { get; set; }
public DbSet<Contrat> Contrats { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite(@"Data Source=db/Notaire.db");
}
public class Paquet
{
public int PaquetId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public string Volume { get; set; }
public string Page { get; set; }
public string Etat { get; set; }
public List<Contrat> Contrats { get; } = new List<Contrat>();
}
public class Personne
{
public int PersonneId { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Nom_pere { get; set; }
public PieceIdentite Piece_identite { get; set; }
public string Num_piece { get; set; }
public string Lieu_naissance { get; set; }
public string Date_naissance { get; set; }
public string Commune { get; set; }
public string Numero_acte { get; set; }
public string Laiv_carte { get; set; } //??????????????
public string Adresse { get; set; }
public string Nationalite { get; set; }
public string Fonction { get; set; }
}
public class Contrat
{
public int ContratId { get; set; }
public string Numero { get; set; }
public DateTime Date { get; set; }
public List<Personne> Partie_1 { get; set; }
public List<Personne> Partie_2 { get; set; }
public int PaquetId { get; set; }
public Paquet Paquet { get; set; }
}
观看次数:
PaquetsView.xaml(这是所有paquets的视图)
<ScrollViewer Background="#EBEEF5" HorizontalScrollBarVisibility="Disabled"
FlowDirection="RightToLeft">
<ItemsControl x:Name="PaquetsControl" Padding="4">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:PaquetControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--<controls:PaquetControl/>
<controls:PaquetControl/>-->
</ItemsControl>
</ScrollViewer>
我在 PaquetsView.xaml.cs 中像这样绑定它的 ItemsSource :
public partial class PaquetsView : UserControl
{
private NotaireDBContext db = new NotaireDBContext();
public PaquetsView()
{
InitializeComponent();
PaquetsControl.ItemsSource = (from p in db.Paquets select p).ToList();
}
}
PaquetView.xaml的DataTemplate -> ItemsControl位于另一个xaml文件中(PaquetControl.xaml),这是一个UserControl,由带有Menu(和菜单项)的TextBlocks和Buttons组成,即显示数据保存在Paquet上,应该可以edit/delete表示Paquet。 其中一部分:
...
<Button x:Name="MoreButton" Style="{DynamicResource MoreButtonTemplate}"
Grid.Column="2" Click="MoreButtonClicked" Margin="0,-4,-4,0">
<Button.ContextMenu>
<ContextMenu Background="White" FlowDirection="RightToLeft">
<MenuItem Header="Edit" Click="EditMenuItemClick"/>
<MenuItem Header="Archive" Click="ArchiveMenuItemClick"/>
<MenuItem Header="حذف" Click="DeleteMenuItemClick"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
...
<TextBlock Grid.Column="0" Text="{Binding Path=Numero}" FontSize="22" Foreground="Black"/>
...
<TextBlock Grid.Row="1" Text="{Binding Path=Date, StringFormat=yyyy/MM/dd}"
Foreground="Black" FontSize="16"/>
...
<!--other TextBlock binded-->
现在我想知道如何通过更新视图使其增删改查。
总而言之,我有一个用于数据持久化的 SQLite 数据库(代码优先),我可以使用 DBContext 获取该数据,但现在我发现最好使用 MVVM 而不是每次都创建 DBContext。
这是一个很大的话题,我怀疑它是否适合这里采用的格式。
因此,我将仅简要概述要点。
通常使用 MVVM 模式实现 WPF。
它是一个严格的 3 层架构:View (WPF) -> ViewModel -> Model。
模型负责处理“真实”数据——这就是所谓的业务逻辑。
View负责创建GUI.
WPF 的特殊之处在于 UI 元素本身通过绑定请求它们需要的数据。
绑定(大部分)创建到 DataContext。
所以,有必要放一些特殊的自定义类型放在那里,负责View和Model之间的链接。
这种类型称为 ViewModel。
在典型的实现中,模型基本上通过方法接收 / returns 数据。
绑定需要属性。
因此,ViewModel 的主要功能之一就是在其属性中提供 View 所需的所有数据。
当应用程序使用数据库时,在 Sharpe 中习惯于在存储库(数据)模式中实现它。
从 MVVM 的角度来看,这样的 Repository 是 Model 的一部分。
但是,为了更简单的理解,为了便于软件维护,Repository通常是在一个单独的层中实现的。
结果,我们得到了一个四层架构:View -> ViewModel -> Model -> Repository。
根据 OOP 的规则和原则,SOLID 在分层架构中,每一层“知道”(具有信息)仅关于底层。
并且所有非public信息都必须封装在层内。
EF 实体反映数据库数据,它们是可变的,可以有相应的属性。
更改源时,这些类型可以更改。
假设您在某个时候想要使用一组 XML 文件而不是数据库。
他们需要不同类型的实体。
因此,此类实体是存储库的内部实现。
要与模型交换数据,存储库必须是模型类型或通用 DTO 类型。
在下一级,ViewModel 还必须从模型接收数据。
但是这里不能使用模型类型,因为它们可以隐式地与业务逻辑相关联,并且有可能创建寄生连接,从而导致不可预测的错误。
在此级别(ViewMode-> Model),专门使用 DTO 类型进行数据交换。
它们最好是不可变的。
View 与 ViewModel 的下一级交换。
首先,GUI 通常需要可变属性。为了自动更新 属性 视图,类型必须实现 INotifyPropertyChanged。
其次,要从 GUI 调用操作,ViewModel 必须在其属性中提供 COMMANDS - 这是 ICommand 实现。
对于我在这里的回答,我使用
第三,在View的类型中,通常需要额外的属性来确保GUI操作的逻辑:一个被选择的元素,一个展开的元素,而不是记录的Id,一个反映它的元素等
由于这些原因,在 ViewModel 级别,您已经需要自己的类型和 DTO 以外的实现。
作为上述结果,我们可以得到反映数据库中某些记录的类型的四种不同实现。
每种类型的使用面积都会在一层或两层。
为了不混淆这一切,最好在一个单独的项目中做每一层。
仅在层内使用的类型在此项目中实现。
多个层使用的类型(例如,DTO)在单独的库中实现(可能用于简单任务和一个公共库)。
为了保持抽象,最好通过接口的初步声明来完成所有的实现。
并通过这些接口在层与层之间传递信息。