Click here to Skip to main content
Click here to Skip to main content

Split ViewModel

, 13 May 2010
Rate this:
Please Sign up or sign in to vote.
Implementing the MVVM Design Pattern with Silvelight - RIA Services.

Introduction

This article provides some insight to the way ViewModel is used in WPF/Silvelight apps. It also derives a practical design pattern for implementing MVVM - a design pattern that applies especially for Silverlight and other RIA applications.

Prerequisites

This article assumes you are somewhat familiar with Silverlight, the MVVM design pattern, and WCF RIA Services. If this is not the case, try stating here: SilverLight at Wikipedia, Microsoft sponsored developer resources site, MVVM design pattern explained and implemented in a Microsoft article, WCF RIA Services resources. It is also recommended you watch this video with Tim Huer demonstrating the use of Silverlight with RIA Services.

An MVVM example

Let us assume that we have a simple model including two tables, one of Tasks and the other of Subtasks. Both tables have a record UID field (an int or a Guid probably), and the Subtasks table has a field pointing to the subtask's parent task. Let us now assume we used our favorite ORM to create the appropriate data model. Here is the diagram created for such a model using the Entity Framework:

Alternate Text

For the moment, let's ignore the whole Silverlight Server-Client ordeal, and think of a plain old WPF ViewModel class we would generate to expose our task to our view. In this example, we inherit a ViewModel base class that provides some framework services (probably an implementation of the INotifiyPropertyChanged interface), takes our model as a constructor parameter, and exposes it for direct binding:

public class TaskViewModel : ViewModelBase
{
    Task _task;

    public TaskViewModel(Task task)
    {
        _task = task;
    }

    public Task Task
    {
        get
        {
            return _task;
        }
    }
}

So far, so good. Now, let's assume that our UI is required to display a list of tasks that should look like this:

No problem. The task list view would probably have a ViewModel of its own that would expose an IEnumerable<TaskViewModel>. The data would be retrieved using the object model, and the Task classes would be wrapped in TaskViewModel classes and served for binding, maybe using the Singleton pattern, like so:

List<TaskViewModel> _taskViewModels;

public IEnumerable<TaskViewModel> TaskViewModels
{
    get
    {
        if (_taskViewModels == null)
        {
            _taskViewModels = tasksContext.Tasks
                .Select(task=> new TaskViewModel( task ) }).ToList();
        }
        return _taskViewModels;
    }
}

The DataGrid or whatever control on the view would bind to the IEnumerable, and all would be well.

At this point, however, we might be called to the boss' office where the boss would "redesign" the UI to include the number of sub tasks for each task in the data displayed:

OK. No problem, we can add a SubtasksCount property to the ViewModel like this:

public class TaskViewModel : ViewModelBase
{
    ...    

    public int SubasksCount
    {
        get
        {
            return _task.Subtasks.Count();
        }
    }
}

Here comes the point: this will all work, and would probably work quite well for WPF applications with local data access. However, it is terribly inefficient as the binding mechanism will call this property and run a query for each task to find the count of its subtasks. Worse, in the absence of a caching mechanism of some sort, the UI might run this query-per-task much more than once, as WPF data binding is the kind of gal that drops by from time to time to see what's new. This, of course, would be much worse when, for each query, we have to go through the process of communicating with the server back and forth.

Note that I assume here that subtasks are not loaded with the tasks; otherwise, I would be using the _task.Subtasks.Count property and no further querying would be needed. My assumption is that you would prefer not to load all subtasks just in order to show their count. In some cases, you would be bringing the subtasks along anyway, so there is no problem, but it is easy to see that some things in some scenarios are better left for SQL Server.

What to do?

Back to the SQL mind set. We would like to query the database once and retrieve both the task data and the count of subtasks. Something like (forgive my poor SQL):

SELECT Tasks .*,
                      (SELECT COUNT(1) 
                       FROM      Subtasks
                       WHERE   (TaskID = Tasks.ID)) AS SubtasksCount
FROM     Tasks

The LINQ equivalent for this would be a query with a projection, something like this:

var tasks = tasksContext.Tasks
    .Select(
        task => new { 
            Task = task, 
            SubasksCount = task.Subtasks.Count() });

Much better. Now we could provide this tasks collection as the source of the binding. But our ViewModel has a ton of things it is supposed to do, so we cannot just do with the anonymous type LINQ projects for us. We would have to use our view model class and initialize it in the query. We would replace the Subtask property with a regular property that would hold the value for us:

public class TaskViewModel : ViewModelBase
{
    public TaskViewModel()
    {
    }

    public Task Task
    {
        get;
        set;
    }

    public int SubasksCount
    {
        get;
        set;
    }
}

And to get the data wrapped in a view model:

var tasks = tasksContext.Tasks.Select
        (task => new TaskViewModel 
            {   Task = task, 
                SubasksCount = task.Subtasks.Count() });

Note that the new version of the ViewModel is no longer immutable, which is not optimal; however, the Entity Frmaework only projects to new objects instantiated with parameterless constructors, so...

Put shortly

Querying the server once-per-record for data that should have been retrieved with the object is inefficient. This data can be data related to child records, as in this example, and it might also be data from parent records. It is possible to bring the other entities, but in most realistic cases, where objects are complex and come in large numbers, the job should be left to the query (unless you really do need the objects for later use).

Back to Silverlight RIA Services

All of the above applies to a Server-Client model with the extra weight of communication overheads (both in time and cost). This is why the same approach should apply to building MVVM applications in Silverlight. But, here we have to deal with the architecture of the system. The rest of this article would deal with implementing this design pattern using Silverlight with RIA Services (if you are a WPF person, you can stop reading now and jump to the end to slay this bizarre idea in the comments).

Split ViewModel

So, we would like to populate our view model properties off the database using LINQ against the data model. But hey, we get our data pre-packaged form the RIA Service, in the form of our exposed model (you might have noticed that the EntitySet collections exposed by the DomainContext have a Select() extension method; unfortunately, this can only project the entity type).

It seems this is heading in the bizarre direction of having the view model defined in the server, as we are definitely going to have it on the client to which we are actually splitting the view model, hence 'Split ViewModel'. The workflow would be something like this:

  • Create data model (ORM)
  • Create view model's server side
  • Expose view models with RIA services
  • Use a partial proxy class on the client side as a basis for the ViewModel you would bind the UI to

You could, of course, expose the Model itself if it is appropriate, for some of the cases.

There are some things you might need to know in order to implement this design pattern, so let us continue with the Tasks example and finish it 'Split ViewModel' style:

Create view models server side

First, the TaskViewModel we created on the server should be prepared for transportation by using the DataContext and DataMember attributes. Also, to have the Task property correctly understood by the RIA Services, we have to decorate it with AssociationAttribute from the System.ComponentModel.DataAnnotations namespace and with IncludeAttribute from the System.ServiceModel.DomainServices.Server namespace. Last but not least, we must have a key property, decorated with the Key attribute. I used the task's ID:

[DataContract]
public class TaskViewModel : ViewModelBase
{
    public TaskViewModel()
    {
    }

    [DataMember]
    [Association("TaskViewModel", "ID", "ID")]
    [Include]
    public Task Task
    {
        get;
        set;
    }

    [DataMember]
    public int SubasksCount
    {
        get;
        set;
    }

    [DataMember]
    [Key]
    public Guid ID
    {
        get
        {
            return Task.ID;
        }
        set
        {
        }
    }
}

Expose view models with RIA services

To expose the view model, the domain service must expose a method returning IQueryable<TaskViewModel>. In this method, we query the database and create the view models, including the number of subtasks:

public IQueryable<TaskViewModel> GetTaskViewModels()
{
    return this.ObjectContext.Tasks.Select
        (task => new TaskViewModel() 
            {   Task = task, 
                SubasksCount = task.Subtasks.Count() 
            });
}

Since the subtasks count is read only, the server CUD methods (Create, Update, Delete) are only required to perform the action on the task object returned in the view model:

public void InsertTaskViewModel(TaskViewModel taskViewModel)
{
    InsertTask(taskViewModel.Task);
}

public void UpdateTaskViewModel(TaskViewModel currentTaskViewModel)
{
    UpdateTask(currentTaskViewModel.Task);
}

public void DeleteTaskViewModel(TaskViewModel taskViewModel)
{
    DeleteTask(taskViewModel.Task);
}

An alternative to this method would be to call the Task CUD methods from the client and not expose CUD methods for the TaskViewModel. This might be somewhat less preferable as you would have to mix some concerns on your client and have less control on the server side. Also, this would make it more difficult to use the DomainDataSource object if that is your preference.

Use a partial proxy class on the client side as the basis for the ViewModel you would bind the UI to

If we now build the project, we get the proxy on the server side. This means, we can do with it anything we could do with a model object, including, for instance, dragging it from the data sources pane onto a page:

Dragging and dropping from the data sources pane to the views would probably not yield a very maintainable application, but, for demo purposes, let's do just that. The tools would generate a grid with a column for each exposed property (excluding the task). If we would now like to add a column displaying the task name, we would need to bind to its path:

<sdk:DataGrid AutoGenerateColumns="False" 
          ItemsSource="{Binding ElementName=taskViewModelDomainDataSource, Path=Data}" 
          Name="taskViewModelDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected">
    <sdk:DataGrid.Columns>
        <sdk:DataGridTextColumn x:Name="iDColumn" 
             Binding="{Binding Path=ID, Mode=OneWay}" Header="ID" 
             IsReadOnly="True" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="taskNameColumn" 
             Binding="{Binding Path=Task.Text}" Header="Task" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="subasksCountColumn" 
             Binding="{Binding Path=SubasksCount}" 
             Header="Subasks Count" Width="SizeToHeader" />
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

It is, of course, possible to extend the client side partial ViewModel proxy to include commands and other logic that might be useful.

Loose ends

  • As you might have noticed, the technologies involved had me exposing all data as settable properties in a way that was not appropriate. This would have the client designer responsible for designing the views to disable unwarranted access.
  • Using the client side proxy as the ViewModel means you cannot derive your own base class for your view model. This is a serious issue as most MVVM frameworks have the ViewModel base do some work.

Final note

This whole thing sounds to me a bit off on one hand, but on the other hand, it seems to fit the bill very well. The ViewModel is a display layer element, hence, it is supposedly a client side element, but it seems that without moving some of it to the server, you cannot have what you need. I would very much like to hear what you think – please vote and comment.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Asher Barak
Software Developer Ziv systems, Israel
Israel Israel
Starting with Apple IIe BASICA, and working my way through Pascal, Power Builder, Visual basic (and the light office VBA) C, C++, I am now a C# .NET developer and designer (and a big fan of the .NET framework).
 
I am currently leading a major effort writing a new framework for SAP Business One extensions and overseeing the development of four products on the same (yet unfinished) framework

Comments and Discussions

 
GeneralWeb Project PinmemberMember 331337726-Aug-10 3:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 13 May 2010
Article Copyright 2010 by Asher Barak
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid