如何将 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)在单独的库中实现(可能用于简单任务和一个公共库)。
为了保持抽象,最好通过接口的初步声明来完成所有的实现。
并通过这些接口在层与层之间传递信息。