Skip to content

02 Saving from client

Matthieu MEZIL (MSFT) edited this page Jun 5, 2015 · 11 revisions

Note that many pieces of code in this workshop are not good for an MVVP point of view. This is assumed for a pedagogic point of view and you will show you in a future workshop how to refactoring this code to be in accordance with MVVM pattern.

In the previous workshop, you saw how to query our entities from the client.

Now you will see how to save your changes (with update only in this workshop).

Create a new window CustomerWindow.
For this, right click on the client project / Add / Window...

Then type CustomerWindow and click on Add

Now create a new class CustomerWindowViewModel and use WAQS context menu to init this ViewModel and do not associate it to any window.

Then update the class to use this code:

public class CustomerWindowViewModel : ViewModelBase
{
    private INorthwindClientContext _context;
    private CustomerWindow _customerWindow; // using the window here is not good for an MVVM point of view

    public CustomerWindowViewModel(INorthwindClientContext context, string customerId, CustomerWindow customerWindow): base (context)
    {
        _context = context;
        _customerWindow = customerWindow;
        LoadCustomerAsync(customerId).ConfigureAwait(true);
    }

    private Customer _customer;
    public Customer Customer
    {
        get { return _customer; }
        private set
        {
            _customer = value;
            NotifyPropertyChanged.RaisePropertyChanged(nameof(Customer));
        }
    }

    private async Task LoadCustomerAsync(string customerId)
    {
        Customer = await _context.Customers.AsAsyncQueryable().FirstOrDefault(c => c.Id == customerId).ExecuteAsync();
    }

    private RelayCommand _saveCommand;
    public ICommand SaveCommand
    {
        get { return _saveCommand ?? (_saveCommand = new RelayCommand(() => SaveChangesAsync().ConfigureAwait(true))); }
    }

    private async Task SaveChangesAsync()
    {
        try
        {
            await _context.SaveChangesAsync();
            _customerWindow.Close();
        }
        catch (Exception e)
        {
            System.Windows.MessageBox.Show(e.Message); // this is not good for an MVVM point of view
        }
    }

    private RelayCommand _cancelCommand;
    public ICommand CancelCommand
    {
        get { return _cancelCommand ?? (_cancelCommand = new RelayCommand(() => _customerWindow.Close())); }
    }
}

Saving changes is as easy as you probably expect it: as with Entity Framework, if your entity in link to a client context, its changes will be automatically tracked and you have nothing special to do for it.

Note that WAQS asynchronous LINQ, the FirstOrDefault method (as any other scalar methods) does not run the query as it does on "standard" LINQ. You have to use ExecuteAsync then.
We will see in a future workshop why it's more interesting with this way.

Now in CustomerWindow.xaml, use this xaml.

In the MainWindowViewModel, add a command to edit a customer

private RelayCommand _editCommand;
public ICommand EditCommand
{
    get
    {
        return _editCommand ?? (_editCommand = new RelayCommand(c =>
          {
              var customerWindow = new CustomerWindow(); // this is not good for an MVVM point of view
              var customerWindowViewModel = new CustomerWindowViewModel(_context, ((CustomerInfo)c).Id, customerWindow);
              customerWindow.DataContext = customerWindowViewModel;
              customerWindow.Show();
          }));
    }
}

And a Refresh method:

public async Task RefreshAsync()
{
    await LoadCustomersAsync();
}

Then in the MainWindow, add a Refresh button and add an edit button in the ListBox ItemTemplate.

Last point, add this method to the MainWindow.xaml.cs. It will be called after clicking on the new Refresh button

private async void RefreshClick(object sender, RoutedEventArgs e)
{
    Cursor = Cursors.Wait;
    await ((MainWindowViewModel)DataContext).RefreshAsync();
    Cursor = Cursors.Arrow;
}

Now you are almost done but you have a bug!

Indeed, if you cancel a customer window and then save a new one, you will save the cancelled changes. In the same way, if you open many customer windows and save one of them, you will also save the changes made in the other one.

To avoid this, the easiest way is to use a specific context per window.

For this, we can use the ClientContextFactory class.

var customerWindowViewModel = new CustomerWindowViewModel(
    ClientContextFactory<INorthwindClientContext>.Create(), 
    ((CustomerInfo)c).Id, 
    customerWindow);

Here we are. you can now update your customer and save changes.

Your solution should be similar to this one.