如何将 MVVM 与 EF 一起使用(在 SQLite 中使用非常复杂的数据库)?

Posted

技术标签:

【中文标题】如何将 MVVM 与 EF 一起使用(在 SQLite 中使用非常复杂的数据库)?【英文标题】:How to use MVVM with EF (with a pretty complex database in SQLite)? 【发布时间】:2021-08-23 20:49:19 【问题描述】:

我正在尝试拥有一个 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) 中,该文件是一个用户控件,由 TextBlocks 和带有菜单(和菜单项)的按钮组成,显示 Paquet 中保存的数据,以及应该能够编辑/删除所说的 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-->

现在我想知道如何通过更新视图使其成为 CRUD。

总结一下,我有一个用于数据持久性的 SQLite DB(代码优先),我可以使用 DBContext 获取该数据,但现在我发现使用 MVVM 比每次都创建 DBContext 更好。

【问题讨论】:

如果你说俄语,那么这里有一个有用的链接cyberforum.ru/wpf-silverlight/…如果你不明白,那就写吧。我会尽量澄清。 首先,@EldHasp,非常感谢,我尝试使用浏览器翻译阅读它,但没有太大帮助。因此,如果您能澄清一下,将不胜感激。 【参考方案1】:

这是一个很大的话题,我怀疑它是否适合这里采用的格式。 因此,我将仅简要概述要点。

习惯上使用 MVVM 模式来实现 WPF。 它是一个严格的 3 层架构:View (WPF) -> ViewModel -> Model。 模型负责处理“真实”数据——这就是所谓的业务逻辑。 View 负责创建 GUI。 WPF 的特点是 UI 元素本身通过绑定请求所需的数据。 (主要)为 DataContext 创建绑定。 因此,有必要在其中放置一些特殊的自定义类型,负责 View 和 Model 之间的链接。 这种类型称为 ViewModel。 在典型的实现中,模型基本上是通过方法接收/返回数据。 绑定需要属性。 因此,ViewModel 的主要功能之一就是在其属性中提供 View 所需的所有数据。

当应用程序使用数据库时,Sharpe 习惯于在存储库(数据)模式中实现这一点。 从 MVVM 的角度来看,这样的存储库是模型的一部分。 但是,为了更简单的理解,为了便于软件维护,Repository 通常在单独的层中实现。 结果,我们得到了一个四层架构:View -> ViewModel -> Model -> Repository。

根据 OOP 的规则和原则,SOLID 在分层架构中,每一层只“知道”(拥有信息)底层。 并且所有非公开信息都必须封装在层内。

EF 实体反映数据库数据,它们是可变的,并且可以具有相应的属性。 更改源时,这些类型可以更改。 假设您在某个时候想要使用一组 XML 文件而不是数据库。 他们需要不同类型的实体。 因此,此类实体是存储库的内部实现。 并且要与模型交换数据,存储库必须是模型类型或通用 DTO 类型。

在下一级,ViewModel 还必须从 Model 接收数据。 但是这里不能使用模型类型,因为它们可以与业务逻辑隐式关联,并且有可能创建导致不可预知的错误的寄生连接。 在此级别(ViewMode-> Model),仅 DTO 类型用于数据交换。 它们最好是不可变的。

View 与 ViewModel 的下一级交换。 首先,GUI 通常需要可变属性。要自动更新属性视图,该类型必须实现 INotifyPropertyChanged。 其次,要从 GUI 调用操作,ViewModel 必须在其属性中提供 COMMANDS - 这是 ICommand 实现。 对于我在这里的答案,我使用的是BaseInpc and RelayCommand classes。 第三,在 View 的类型中,往往需要额外的属性来保证 GUI 操作的逻辑:一个被选中的元素,一个展开的元素,而不是记录的 Id,一个反映它的元素等。 由于这些原因,在 ViewModel 级别,您已经需要自己的类型以及 DTO 以外的实现。

基于上述结果,我们可以得到四种不同的实现,用于反映数据库中某些记录的类型。 每种类型的使用区域将在一层或两层。 为了不让这一切感到困惑,最好将每一层都放在一个单独的项目中。 仅在层内使用的类型在此项目中实现。 多个层使用的类型(例如,DTO)在单独的库中实现(可能用于简单任务和一个通用库)。 为了保持抽象,最好通过接口的初步声明来完成所有实现。 并通过这些接口在各层之间传递信息。

【讨论】:

以上是关于如何将 MVVM 与 EF 一起使用(在 SQLite 中使用非常复杂的数据库)?的主要内容,如果未能解决你的问题,请参考以下文章

使用 MVVM 创建具有现有外部属性的 EF Core 实体

如何将 RelayCommand 与 MVVM Light 框架一起使用

如何首先将 EF Core 代码与 azure synapse 一起使用

EF Core 5 - 如何将 EF.Functions.Like 与映射到 JSON 字符串的自定义属性一起使用?

如何将 Entity Framework 4.1 与 MVC 3 登录一起使用

将 MVVM 架构中的 Tab 布局与数据绑定库一起使用