Mohtaa
Mohtaa

Reputation: 129

Implementing the MVVM Pattern with Entity Framework - Add Removing

I'm trying to implement the MVVM Pattern on my WPF project that uses Entity Framework to get / manipulates data. I'm confused, i want to know where validating changes on my collections of model objects to database should be??? my application is as follow : in my view i've a datagrid of persons, two textboxes that will load the name / surname of the selected person , a buton to updated changes of rows and a button to delete the selected row.

In my ModelView , i have an observableCollection that will be loaded at the initilization of the class with information from my database ( entities) + two Relaycommands for Add/Remove buttons ( please find the code below).

The problem is that i didn't understood well the philosophy of MVVM, where and when and how modifications on my data should be pushed to database? For now, when i update a row in my database, and save my DB context modifications on my observable collection are submitted, but it's not the cdase when i remove an item, i've to manually look for it in database and remove it ( i've attached my observablecollection to a NotifyCollectionChangedEventHandler event that will handle this)... i don't get the point of using Observable collection!!!

Is there any simple explanation of a "perfect" mvvm architecture using database data??? Thanks! my ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows;
using MVVMOK.Models;
using MVVMOK.Base;
using System.Collections.Specialized;

namespace MVVMOK.ViewModel
{
    class MainWindowViewModel : ViewModelBase
    {
        private DW_MargoEntities contexte;



        //Constructor
        public MainWindowViewModel()
        {
            contexte = new DATABASEEntities();
            collectionOfCollaborators = new ObservableCollection<Collaborator>();
            foreach (Collaborator c in contexte.Collaborator)
            {
                collectionOfCollaborators.Add(c);
            }


            //Abonnement pour l'ajout ou la suppression d'éléments :
            collectionOfCollaborators.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collectionOfCollaboratorsChanged);

            //Assignation des commandes : 
            this._deleteComand = new RelayCommand(new Action<object>(DeleteRow));
            this._updateCommand = new RelayCommand(new Action<object>(UpdateDB));
        }
        //liste des propriétés publiques:


        //Propriété pour représenter l'élément séléctionné du datagrid
        private  Collaborator selectedItem;
        public Collaborator _selectedItem
        {
            get { return selectedItem; }
            set
            {
                if (value != selectedItem)
                {
                    selectedItem = value;
                    OnPropertyChanged("_selectedItem");
                };
            }
        }
        //Propriété pour représenter l'élément séléctionné:
        private ObservableCollection<Collaborator> collectionOfCollaborators;
        public ObservableCollection<Collaborator> _collectionOfCollaborators
        {
            get { return collectionOfCollaborators; }
            set
            {
                this.collectionOfCollaborators = value;
                OnPropertyChanged("_collectionOfCollaborators");
            }
        }

        //Commandes : 
        public ICommand _updateCommand
        {
            get;
            set;
        }
        public ICommand _deleteComand
        {
            get;
            set;
        }

        void collectionOfCollaboratorsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Collaborator f = new Collaborator();
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:

                    for(int i = 0; i<e.NewItems.Count;i++)
                    {

                        if (e.NewItems[i].GetType().Equals(f.GetType()))
                        {
                            contexte.Collaborator.Add(e.NewItems[i] as Collaborator);
                        }
                    }
                    contexte.SaveChanges();
                    break;

                case NotifyCollectionChangedAction.Remove:

                    for (int i = 0; i < e.OldItems.Count; i++)
                    {

                        if (e.OldItems[i].GetType().Equals(f.GetType()))
                        {
                            contexte.Collaborator.Remove(e.OldItems[i] as Collaborator);
                        }
                    }
                    contexte.SaveChanges();
                    break;
                //Reset = Clear

            }
        }



        //Services :
        public void UpdateDB(object msg)
        {
            contexte.SaveChanges();
        }


        public void DeleteRow(object msg)
        {

            _collectionOfCollaborators.Remove(_selectedItem);
            contexte.SaveChanges();
        }
    }
}

My model

namespace MVVMOK.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Collaborator
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
    }
}

My XAML

<Window x:Class="MVVMOK.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:MVVMOK.ViewModel"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <!-- Declaratively create an instance of our SongViewModel -->
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid Height="237" HorizontalAlignment="Left" Margin="12,12,0,0" Name="grid1" VerticalAlignment="Top" Width="479">
            <DataGrid AutoGenerateColumns="False" Height="237" HorizontalAlignment="Left" Name="dataGrid1" VerticalAlignment="Top" Width="479" ItemsSource="{Binding Path=_collectionOfCollaborators, Mode=TwoWay}" SelectionMode="Single" SelectedItem="{Binding Path=_selectedItem, Mode=TwoWay}"  >
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Path=Id}" Header="ID" />
                    <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" Width="4*" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="104,255,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text ="{Binding Path=_selectedItem.Name, Mode=TwoWay}" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="104,283,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" Text ="{Binding Path=_selectedItem.Surname, Mode=TwoWay}" />
        <Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="50,255,0,0" Name="label1" VerticalAlignment="Top" />
        <Label Content="Surname" Height="28" HorizontalAlignment="Left" Margin="37,283,0,0" Name="label2" VerticalAlignment="Top" />
        <Button Content="Delete Row" Height="23" HorizontalAlignment="Left" Margin="416,260,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding Path=_deleteComand}"/>
        <Button Content="Update All" Height="23" HorizontalAlignment="Left" Margin="335,260,0,0" Name="button2" VerticalAlignment="Top" Width="75" Command="{Binding Path=_updateCommand}"/>
    </Grid>
</Window>

Upvotes: 1

Views: 9001

Answers (2)

silverfighter
silverfighter

Reputation: 6882

I think there is not "THE" way in mvvm there are always several and like always it depends.

But the to your solution there is an interface called IDataErrorInfo which can help you out.

For a example take a look at this article... http://www.codeproject.com/Articles/98681/Validating-User-Input-WPF-MVVM

Or here: http://blindmeis.wordpress.com/2012/04/16/wpf-mvvm-idataerrorinfo-validation-errortemplate/

And maybe you should think about hiding entity framework from your view models with the repository pattern or a simply DataService.

hope that helps...

Upvotes: 0

user663470
user663470

Reputation: 149

I don't have an answer to why you aren't seeing your expected results but I thought I would give you some ideas on how to structure this a bit better.

What you really want to do when data changes is apply a command. For instance the delete command should be applied. When handling this command you can perform any validation and either persist the change or flag an error. So your DeleteRow class which you have wrapped in the relay command should really be a separate class in a Model tier of your application. You can have a web tier that uses MVVM, but it should be persistence ignorant, it should apply a command and then react appropriately if the command succeeds or fails.

this._deleteComand = new RelayCommand(new Action<object>(DeleteRow));

could be something more like

this._deleteComand = new RelayCommand(new Action<object>() => {new DeleteCommandHandler().Apply(new DeleteCommand(){SelectedItem = _selectedItem})});

The DeleteCommandHandler and DeleteCommand will form part of your model (I would use a different class library for this).

Now your handlers need to know about your persistence mechanism, so inside your handler you can create your DW_MargoEntities context and do the work to delete.

The advantages of this is your view model is no longer responsible for updating your model so your view models will be a lot simpler and won't have a direct dependency on what database you use.

Once you have a command handler pattern setup I would move towards dependency injection for your context.

For instance rather than saying new DeleteCommandHandler(), you could ask an Inversion of Control container such as Structure Map to build it.

ObjectFactory.GetInstance<DeleteCommandHandler>();

Then your DeleteCommandHandler class could look like

class DeleteCommandHandler
{
    DW_MargoEntities context;
    public DeleteCommandHandler(DW_MargoEntities context)
    {
        this.context = context;
    }

    public bool Apply(DeleteCOmmand command)
    {
        if (not valid) {return false;}
        var item = context.LoadEntityById<EntityTypeHere>(command.SelectedItem) // Query the item

        context.Delete(item);
        return true;
    }
}

Sorry for being a bit vague, it's a lot to explain and it's getting a bit late here. Have a read on the command pattern http://en.wikipedia.org/wiki/Command_pattern

Upvotes: 1

Related Questions