5
votes

I am trying to Save Data to database.

Suppose I have a Table Named Customers having three fields:

Id
FirstName
LastName

I have created my models using ADO.Net Entity Data Model.

Here is my ViewModel code

public class myViewModel : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            _firstName = value;
            OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get
        {
            return _lastName;
        }
        set
        {
            _lastName = value;
            OnPropertyChanged("LastName");
        }
    }

    protected virtual void OnPropertyChanged(string PropertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

Here is my MainWindow.xaml file:

<Window x:Class="Lab_Lite.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:Lab_Lite.ViewModels"
        Title="MainWindow" Height="350" Width="525" WindowState="Maximized">

    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBlock Grid.Row="0" Grid.Column="0" Text="FirstName" />
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />

        <TextBlock Grid.Row="1" Grid.Column="0" Text="LastName" />
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />

        <Button Grid.Row="2" Grid.Column="1" Content="Save" />
    </Grid>
</Window>

I have got two problems here:

1. How my ViewModel knows that FirstName property declared in ViewModel is 
   referenced to FirstName Column in my database?
2. How to save changes to database when UpdateSourceTrigger is set to Explicit?

I think I have found the answer to 2nd question upto some extent using Command. But I dont know if it is right or not as I dont know the answer to my first question.

Update:

Suppose I have two tables like this:

Customer:

CustomerID
Name
GenderID //Foreign Key

Gender:

GenderID
Value

Now what should be the value of CurrentCustomer.Gender in SaveCustomerChanges Method?

1

1 Answers

13
votes

I highly recommend you use a ORM like Entity Framework (EF) using database-first since you've already created a database. Entity Framework will create your POCOs (model classes, Plain Old C# Objects) automatically for you and will also create a class named **YourDbName**Context which is your DbContext. At that point your application will create an instance of you context each time you run your app, and you will be able to access your database using the context. EF will also keep track of any changes you make so here is how you problem would be solved:

EF generates POCO:

public partial class Customer
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

In your viewmodel you can retrieve a List<Customer>() from your database.

public List<Customer> Customers { get; set; } //Load from context in constructor, really though you should do it in a service layer
public Customer CurrentCustomer { get; set; } //Current customer you need to edit

So now you have two options in the viewmodel: In order to be natural MVVM you shouldn't bind CurrentCustomer to the view. That doesn't follow the pattern. In order to fix this, you create separate properties, just like you have above, and you can either return CurrentCustomer.PropertyName in the getter and use that in the setter like so:

public string FirstName
{
    get
    {
        return CurrentCustomer.FirstName;
    }
    set
    {
        CurrentCustomer.FirstName = value;
        OnPropertyChanged("FirstName");
    }
}

By doing this you won't have to worry about mapping the viewmodel property to the POCO property, however any change you make in the view will now cause Entity Framework to track the change, in which case all you need to do now in the save method is call dbContext.SaveChanges();.

If you want to be able to do any kind of cancel or undo then keep your properties set up the way you have it now:

private string _firstName;
public string FirstName
{
    get
    {
        return _firstName;
    }
    set
    {
        _firstName = value;
        OnPropertyChanged("FirstName");
    }
}

and in your save method you will have to map the viewmodel properties to the CurrentCustomer's properties will should then cause a change to be tracked:

private void SaveCustomerChanges()
{
   //Could use Automapper to handle mapping for you.
   CurrentCustomer.FirstName = this.FirstName;
   CurrentCustomer.LastName = this.LastName;
   dbContext.SaveChanges(); //dbContext will be named diff for you
}

At this point the changes should have gone through and updated the database tables.

Now if you're not using Entity Framework, you will need to set up some kind of data access layer to which you can pass in a Customer object and then you will need to retrieve that object from your database and update its values.

Check out this as well!

EDIT: To call SaveCustomerChanges() you will need to wire this method up using ICommand.

First create a RelayCommand Class:

public class RelayCommand : ICommand
{
    private Action<object> _action;

    public RelayCommand(Action<object> action)
    {
        _action = action;
    }

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _action(parameter);
    }

    #endregion
}

Now in your viewmodel:

public ICommand SaveChangesCommand { get; set; }

In your viewmodel constructor

SaveChangesCommand = new RelayCommand(SaveCustomerChanges);

Then wire up SaveChangesCommand in XAML:

<Button Content="Save" Command="{Binding SaveChangesCommand }" />

Now when you click Save the command should fire the SaveCustomerChanges() method.