0

I'm trying to have an MVVM architecture while Models are EF Models too.

In Code :

Model:

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; }
}

Views :

PaquetsView.xaml (this is a view of all 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>

I bind it's ItemsSource like this in PaquetsView.xaml.cs :

public partial class PaquetsView : UserControl
{
    private NotaireDBContext db = new NotaireDBContext();

    public PaquetsView()
    {
        InitializeComponent();
        PaquetsControl.ItemsSource = (from p in db.Paquets select p).ToList();
    }
}

The DataTemplate of PaquetView.xaml -> ItemsControl stand in another xaml file (PaquetControl.xaml), which is a UserControl consisting of TextBlocks and Buttons with Menu (and menu item), that show the data held in on Paquet, and should be able to edit/delete said Paquet. A portion of it :

...
<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-->

Now I would like to know how can I make it CRUD with updates of the view.

Summarize, I have an SQLite DB (code first) for data persistence, I can get that data with DBContext, but now I'm seeing that it's better to use MVVM rather than create DBContext each time.

elena.kim
  • 930
  • 4
  • 12
  • 22
  • If you speak Russian, then here is a useful link for you https://www.cyberforum.ru/wpf-silverlight/thread2563725-page2.html#post14172450 If you do not understand, then write. I will try to clarify. – EldHasp Jun 06 '21 at 18:29
  • First of all, @EldHasp , thanks a lot, I tried to read it with the browser traduction, didn't help that much. So if you would clarify that would be appreciated. – ryad BOUFAR Jun 07 '21 at 12:32

1 Answers1

1

This is a very large topic and I doubt that it will fit the format adopted here.
Therefore, I will briefly outline only the main points.

It is customary to implement WPF using the MVVM pattern.
It is a strict 3-tier architecture: View (WPF) -> ViewModel -> Model. The Model is responsible for working with "real" data - this is the so-called Business Logic. View is responsible for creating the GUI.
The peculiarity of WPF is that the UI elements themselves request the data they need through the bindings.
Bindings are created (mostly) to the DataContext.
Therefore, it is necessary to put some special custom type there, which is responsible for the links between the View and the Model.
This type is called ViewModel.
In a typical implementation, the Model basically receives / returns data via methods.
And bindings need properties.
Therefore, one of the main functions of the ViewModel is to provide all the data necessary for the View in its properties.

When an application works with a database, it is customary in Sharpe to implement this in the Repository (Data) pattern.
From the MVVM point of view, such a Repository is part of the Model. But, for a simpler understanding, to facilitate software maintenance, the Repository is usually implemented in a separate layer.
As a result, we get a four-tier architecture: View -> ViewModel -> Model -> Repository.

According to the rules and principles of OOP, SOLID in a layered architecture, each layer "knows" (has information) only about the underlying layer.
And all non-public information must be encapsulated inside the layer.

EF entities reflect database data, they are mutable, and can have corresponding attributes.
When changing the source, these types can change.
Let's say you, at some point, want to use a set of XML files instead of a database.
And they need entities of a different type.
Therefore, such entities are the internal implementation of the Repository. And to exchange data with the Model, the Repository must be either Model types or generic DTO types.

At the next level, the ViewModel must also receive data from the Model.
But Model types cannot be used here, since they can be implicitly associated with Business Logic and there is a possibility of creating parasitic connections leading to unpredictable bugs.
At this level (ViewMode-> Model), exclusively DTO types are used for data exchange.
They are preferably immutable.

The next level of exchange of View with ViewModel.
First, the GUI often needs mutable properties. And to auto-update the property view, the type MUST implement INotifyPropertyChanged.
Secondly, to call actions from the GUI, the ViewModel must provide COMMANDS in its properties - this is the ICommand implementation.
For my answers here, I am using the BaseInpc and RelayCommand classes.
Thirdly, in types for View, additional properties are often needed to ensure the logic of the GUI operation: a selected element, an expanded element, instead of the Id of the record, an element reflecting it, etc.
For these reasons, at the ViewModel level, you already need your own types with an implementation other than DTO.

As a result of the above, we can get four different implementations for the type reflecting some record in the database.
The area of ​​use of each type will be in one or two layers.
In order not to get confused in all this, it is better to do each layer in a separate project.
Types used only within the layer are implemented in this project.
The types used by several layers (for example, DTO) are implemented in separate libraries (maybe for simple tasks and one common library).
To maintain abstraction, it is desirable to do all implementations through the preliminary declaration of interfaces.
And transfer information between layers through these interfaces.

EldHasp
  • 6,079
  • 2
  • 9
  • 24