Mvvm: Binding to Model While Keeping Model in Sync with a Server Version

MVVM: Binding to Model while keeping Model in sync with a server version

In the past I 've written an application that supports "live" editing of data objects from multiple locations: many instances of the app can edit the same object at the same time, and when someone pushes changes to the server everyone else gets notified and (in the simplest scenario) sees those changes immediately. Here's a summary of how it was designed.

Setup

  1. Views always bind to ViewModels. I know it's a lot of boilerplate, but binding directly to Models is not acceptable in any but the simplest scenarios; it's also not in the spirit of MVVM.

  2. ViewModels have sole responsibility for pushing changes. This obviously includes pushing changes to the server, but it could also include pushing changes to other components of the application.

    To do this, ViewModels might want to clone the Models they wrap so that they can provide transaction semantics to the rest of the app as they provide to the server (i.e. you can choose when to push changes to the rest of the app, which you cannot do if everyone directly binds to the same Model instance). Isolating changes like this requires still more work, but it also opens up powerful possibilities (e.g. undoing changes is trivial: just don't push them).

  3. ViewModels have a dependency on some kind of Data Service. The Data Service is an application component that sits between the data store and the consumers and handles all communication between them. Whenever a ViewModel clones its Model it also subscribes to appropriate "data store changed" events that the Data Service exposes.

    This allows ViewModels to be notified of changes to "their" model that other ViewModels have pushed to the data store and react appropriately. With proper abstraction, the data store can also be anything at all (e.g. a WCF service in that specific application).

Workflow

  1. A ViewModel is created and assigned ownership of a Model. It immediately clones the Model and exposes this clone to the View. Having a dependency on the Data Service, it tells the DS that it wants to subscribe to notifications for updates this specific Model. The ViewModel does not know what it is that identifies its Model (the "primary key"), but it doesn't need to because that's a responsibility of the DS.

  2. When the user finishes editing they interact with the View which invokes a Command on the VM. The VM then calls into the DS, pushing the changes made to its cloned Model.

  3. The DS persists the changes and additionally raises an event that notifies all other interested VMs that changes to Model X have been made; the new version of the Model is supplied as part of the event arguments.

  4. Other VMs that have been assigned ownership of the same Model now know that external changes have arrived. They can now decide how to update the View having all pieces of the puzzle at hand (the "previous" version of the Model, which was cloned; the "dirty" version, which is the clone; and the "current" version, which was pushed as part of the event arguments).

Notes

  • The Model's INotifyPropertyChanged is used only by the View; if the ViewModel wants to know whether the Model is "dirty", it can always compare the clone to the original version (if it has been kept around, which I recommend if possible).
  • The ViewModel pushes changes to the Server atomically, which is good because it ensures that the data store is always in a consistent state. This is a design choice, and if you want to do things differently another design would be more appropriate.
  • The Server can opt to not raise the "Model changed" event for the ViewModel that was responsible for this change if the ViewModel passes this as a parameter to the "push changes" call. Even if it does not, the ViewModel can choose to do nothing if it sees that the "current" version of the Model is identical to its own clone.
  • With enough abstraction, changes can be pushed to other processes running on other machines as easily as they can be pushed to other Views in your shell.

Hope this helps; I can offer more clarification if required.

How to synchronize Model and View Model bidirectional in MVVM?

There is no silver bullet. As model is a representation of the database and viewmodel is more closer to the interface, there is always some business logic needed to convert the model to view model and vice versa.

I usually have two methods in my view model class - SyncModel(ViewModel viewModel) and SyncViewModel(Model model)

One more suggestion -

Model should not implement INotifyPropertyChanged. The view model should implement this as its bound to the user interface. Why does the model ever need to change? It represents whats in the db. You can refresh it but why do you need change notifications for the model?

Edit: MVVM: Binding to Model while keeping Model in sync with a server version

Hard reference. Each class having a reference to another, listens to property change event and updates itself accordingly.

Observer Pattern - Have an observer class, each class register itself with an observer, observer listens for any changes and updates all its subscribers.

There's also an event aggregator which might be useful.

If you want a deferred update, an isDirty property would be required. You know your application better, google for more articles and choose wisely.

MVVM in WPF - How to alert ViewModel of changes in Model... or should I?

If you want your Models to alert the ViewModels of changes, they should implement INotifyPropertyChanged, and the ViewModels should subscribe to receive PropertyChange notifications.

Your code might look something like this:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SomeProperty")
RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

But typically this is only needed if more than one object will be making changes to the Model's data, which is not usually the case.

If you ever have a case where you don't actually have a reference to your Model property to attach the PropertyChanged event to it, then you can use a Messaging system such as Prism's EventAggregator or MVVM Light's Messenger.

I have a brief overview of messaging systems on my blog, however to summarize it, any object can broadcast a message, and any object can subscribe to listen for specific messages. So you might broadcast a PlayerScoreHasChangedMessage from one object, and another object can subscribe to listen for those types of messages and update it's PlayerScore property when it hears one.

But I don't think this is needed for the system you have described.

In an ideal MVVM world, your application is comprised of your ViewModels, and your Models are the just the blocks used to build your application. They typically only contain data, so would not have methods such as DrawCard() (that would be in a ViewModel)

So you would probably have plain Model data objects like these:

class CardModel
{
int Score;
SuitEnum Suit;
CardEnum CardValue;
}

class PlayerModel
{
ObservableCollection<Card> FaceUpCards;
ObservableCollection<Card> FaceDownCards;
int CurrentScore;

bool IsBust
{
get
{
return Score > 21;
}
}
}

and you'd have a ViewModel object like

public class GameViewModel
{
ObservableCollection<CardModel> Deck;
PlayerModel Dealer;
PlayerModel Player;

ICommand DrawCardCommand;

void DrawCard(Player currentPlayer)
{
var nextCard = Deck.First();
currentPlayer.FaceUpCards.Add(nextCard);

if (currentPlayer.IsBust)
// Process next player turn

Deck.Remove(nextCard);
}
}

(Above objects should all implement INotifyPropertyChanged, but I left it out for simplicity)

Interaction of view models in MVVM

You should definitely not make a huge "main view model" -- this is an anti-pattern not dissimilar to a god object.

The key idea here is that your viewmodels do not need to access several properties from other view models; rather, all of the viewmodels need to access specific pieces of information.

Right now you are most likely injecting this information into each viewmodel at the time of instantiation. Instead of doing this, provide each viewmodel with a reference to a service -- an application module that exposes this information through properties and/or methods. The information can be exposed in the form of scalar values if it's extremely simple in nature, or in the form of models if it's anything more complicated than that.

Schematically this would look like

/--------------\
| ViewModelA |
| | <=======\
| | |
\--------------/ | <--- information is pulled /================\
+=========[model]===[model]====== | Service |
/--------------\ | \================/
| ViewModelB | |
| | <=======/
| |
\--------------/

The service should be injected into the viewmodels upon construction (either manually or by the DI container if you are using one). Each viewmodel should only require a reference to the service and enough information to tell to the service which model it's interested in; it will then request that model from the service and populate its "interesting" properties based on that.

This way of doing things is more involved than simply constructing a viewmodel object and setting its properties externally, but it will allow your application to grow in complexity without becoming unmanageable.

For example, if there are lots of views that bind on different viewmodels and these viewmodels depend in complex ways on a number of models, a sane manner to work would be:

  1. A view/viewmodel pair is constructed
  2. The viewmodel requests any models it needs to know about from the service; it subscribes to an event that the service exposes to let subscribers know that a model has changed
  3. Changes are performed to the models through databound controls
  4. The view invokes a "save" command on the viewmodel
  5. In response to that, the viewmodel invokes a "save this model" method on the service
  6. The service persists the information and publishes a "model changed" event (see step 2)
  7. Other viewmodels interested in the same model know that the model has changed and can query the service for its new state

This is possible if everything is routed through the service. Imagine what it would take to keep everything synchronized otherwise -- the only way to cope would be to put all the information into a giant viewmodel and reference that from everything else. Ugly.

MVVM: Modified model, how to correctly update ViewModel and View?

If the view is binding to the Model directly then as long as the service is using the same instance any changes to the model properties will be propogated to the view.

However if you are recreating a new model in the service then you will give the viewmodel the new model. I expect to see the model as a property on the view model so when you set that property all binding should be alerted to the change.

//in the ViewModel
public Person Model
{
get { return _person; }
set { _person = value;
RaisePropertyChanged("Model"); //<- this should tell the view to update
}
}

EDIT:

Since you state there are specific ViewModel logic then you can tailor those properties in the ViewModel

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty");
...
}

public string SpecicalProperty
{
get
{
reutrn Model.Prop1 + " some additional logic for the view";
}
}

IN XAML

  <TextBlock Text="{Binding Model.PropertyDirect}" />  
<TextBlock Text="{Binding SpecicalProperty}" />

This way only both the Model and ViewModel propertys are bound to the view without duplicating the data.

You can get fancier creating a helper to link the property changes from the model to the view model or use a mapping dictionary

 _mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" });

and then find the properties to update by getting the list of properties

 private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{

string[] props;
if(_mapping.TryGetValue(e.PropertyName, out props))
{
foreach(var prop in props)
RaisePropertyChanged(prop);
}
}

MVVM - PropertyChanged in Model or ViewModel?

The INotifyPropertyChanged (INPC) interface is used for Binding.

So, in the average case, you want to implement it in your ViewModel.

The ViewModel is used to decouple the Model from your View, so there is no need to have INPC in your Model, as you do not want Bindings to your Model.

In most cases, even for smaller properties, you still have a very small ViewModel.

If you want a solid base for MVVM, you are probably going to use some kind of MVVM Framework like caliburn.micro. Using it will give you a ViewModelBase (or here NotifyPropertyChangedBase) so that you do not have to implement those interface members yourself and can just use NotifyOfPropertyChange(() => MyProperty), which is way easier and less error prone.

UPDATE
As there seem to be many Windows Forms developers out there, here is an excellent
article that will give deeper understanding of what MVVM is about:
MSDN Magazine on MVVM

I have linked especially the part about the datamodel, that the question is about.

MVVM: ViewModel and Business Logic Connection

Putting business logic inside the viewmodel is a very bad way to do things, so I 'm going to quickly say never do that and move on to discussing the second option.

Putting the logic inside the model is much more reasonable and it's a fine starting approach. What are the drawbacks? Your question says

So if the Renamer Model is triggered to manipulate some Data by Method
Calls, the ViewModel has no Clue which Data is manipulated. Because
the Model doesnt Contain any PropertyChange Notification and I will
avoid that.

Well, making your model implement INotifyPropertyChanged would certainly let you move on to better things. However, it's true that sometimes it is not possible to do that -- for example the model may be a partial class where properties are auto-generated by a tool and don't raise change notifications. That's unfortunate, but not the end of the world.

If you want to buy something then someone has to pay for it; if it's not the model that gives such notifications then you are left with only two choices:

  1. The viewmodel knows which operations on the model (possibly) cause changes and it updates its state after each such operation.
  2. Someone else knows which operations cause changes and they notify the viewmodel to update its state after the model it is wrapping changes.

The first option is again a bad idea, because in effect it is going back to putting "business logic" inside the viewmodel. Not as bad as putting all the business logic in the viewmodel, but still.

The second option is more promising (and unfortunately more work to implement):

  • Put part of your business logic in a separate class (a "service"). The service will implement all business operations you will want to perform by working with model instances as appropriate.
  • This means that the service knows when model properties may change (this is OK: model + service == business logic).
  • The service will provide notifications about changed models to all interested parties; your viewmodels will take a dependency on the service and receive these notifications (so they will know when "their" model has been updated).
  • Since the business operations are also implemented by the service, this remains very natural (e.g. when a command is invoked on the viewmodel the reaction is calling an appropriate method on the service; remember, the viewmodel itself does not know about the business logic).

For more information on such an implementation see also my answers here and here.

WPF MVVM WCF client/server architecture

There are different solutions for the architecture of a 3-tier WPF application, but here is one possibility:

1+2) One solution is to create "intermediate" objects that represent what your client application actually needs.
For instance, if your application needs to display information about a product plus the associated customer name, you could build the following object:

public MyProduct
{
// Properties of the product itself
public int ProductID { get; set; }
public string ProductName { get; set; }
...

// Properties that come from the Customer entity
public string CustomerName { get; set; }
}

You can then expose a stateless WCF service that returns your product from an ID:

[ServiceContract]
MyProduct GetProductByID(int productID);

In the server side of your application (i.e. the implementation of your service), you can return a MyProduct instance build by querying the database through EF (one context per call):

public MyProduct GetProductByID(int productID)
{
using (DBContext ctx = new ....)
{
return from p in ctx.Products
where p.ID == productID
select new MyProduct
{
ProductID = p.ID,
ProductName = p.Name,
CustomerName = p.Customer.Name // Inner join here
};
}
}

3) Adding additional layer between the WCF services and the ViewModel might be considered as over-engineering. IMHO it's OK to call WCF services directly from the ViewModel. WCF generated client proxy code has the actual role of your model (at least one part of your model).


EDIT:

why MyProduct should reference the CustomerName instead of the
Customer.In my case, Customer would have many properties I'd work
with. Woudn't this "mapping" be too expensive ?

You can use the actual entities. But on client side, as it's a 3-tier architecture, you have no access to the DB through the navigation properties. If there was a nested Customer property (of type Customer), the client would have access to theProduct.Customer.Products, which has no sense has you can't lazy load entities this way (no DB context on client side).

Flattened "intermediate" POCOs are much more simple IMO. There is no performance issues, the mapping is straightforward and the CPU usage for this particular operation is infinitesimal compared to the DB request time.



Related Topics



Leave a reply



Submit