views:

188

answers:

1

In my main Window1.xaml.cs, I build an ObservableCollection of ViewModels like this by instantiating with a LINQ-to-SQL model object:

using (var db = Datasource.GetContext())
{
    var customers = from c in db.Customers
                    select c;

    foreach (var customer in customers)
    {
        CustomerCollection.Add(new CustomerModelView(customer));
    }
}

In the constructor of each ViewModel, I save the LINQ-to-SQL object internally and map all the properties from Model to ViewModel:

#region ViewModelProperty: Customer
private Customer _customer;
public Customer Customer
{
    get
    {
        return _customer;
    }

    set
    {
        _customer = value;
        OnPropertyChanged("Customer");
    }
}
#endregion

#region ViewModelProperty: FirstName
private string _firstName;
public string FirstName
{
    get
    {
        return _firstName;
    }

    set
    {
        _firstName = value;
        OnPropertyChanged("FirstName");
    }
}
#endregion

...

public CustomerViewModel(Customer customer)
{
    Customer customer;
    FirstName = customer.FirstName;
    ...
}

The problem is that when handling events in my ViewModel, e.g. after the user changes any of the fields and clicks the Save button, I have to reinstantiate a LINQ-to-SQL object in order to save the changes, which incurs more traffic to and from the database when I actually already had the object saved internally:

using (var db = Datasource.GetContext())
{
    var customer = (from c in db.Customers
                 where c.Id == Id
                 select c).SingleOrDefault();

    customer.FirstName = FirstName;
    db.SubmitChanges();
}

The immediate solution is to pass the LINQ-to-SQL datacontext object down to the ViewModel as well when instantiating the ViewModel, like this:

public CustomerViewModel(Customer customer, DataClasses1DataContext db)
{
    Customer = customer;
    Db = db;

    FirstName = customer.FirstName;
    ...
}

and then when handling my events such as the Save button, I could just call SubmitChanges() on the internal Db variable without reinstantiating it and get the data from the database again.

So it seems I have to either (1) pass a datalayer context object into the ViewModel which doesn't seem like a clean MVVM way of solving this problem, or (2) I have to refetch my LINQ-to-SQL object each time I want to save the model object that I already have saved internally in the ViewModel.

What is the cleanest MVVM approach to solve this dilemma?

+2  A: 

This means your ViewModel has knowledge about the datalayer. I think it's better to use the object initializer context. This also allows you to do this:

using (var db = Datasource.GetContext())
{
    var customers = from c in db.Customers
                    select new CustomerModelView
                    {
                        Name = c.Name;
                        Address = c.Address;
                    };

    CustomerCollection.AddRange(customers);
}

When updating the database, you should be able to create a new object, and then use db.AttachObject to make the context aware of it. That saves you from having to re-fetch it from the DB just to change it.

Sander Rijken
+1 for attach. This is the right approach. Have ViewModels store the model object in a Model property and just use attach when you are round-tripping.
Anderson Imes
@Anderson: I don't think attaching the model object to the viewmodel is a good idea actually. It doesn't get serialized back when round-tripping true a page post, and it also introduces tight coupling of the two. The viewmodel should just contain data, nothing more..
Sander Rijken
It's a *View**Model* This is the point. Both words are important here. I've seen a lot of people introduce another construct like a controller that acts as a lubricating layer between a viewmodel and the model, but I wouldn't consider this a must-have.
Anderson Imes
The problem is that when the viewmodel object gets rendered on the page, and a post is performed, the model object is lost. So *something* needs to recreate the model object. You can't stick the model object on the viewmodel, and expect it to survive a post cycle
Sander Rijken
What is a post? I think you are referring to silverlight? The OP didn't mention silverlight.
Anderson Imes
A post is when you have an html page with a form on it, that you submit. It can be both post or get, but the state of the model is lost there. ASP.NET MVC will try to repopulate the ViewModel's properties with matching post/get variables.
Sander Rijken
Yeah... but that's ASP.NET, not WPF. The OP posted with a WPF tag.
Anderson Imes
Right, I lost that somewhere :-( Thanks for clearing up
Sander Rijken