Architecting Silverlight LOB applications (Part 6) – Building an MVVM Framework


Hello again! In this post I’m going to talk about building an MVVM framework.

As I said in the previous post, this post should be about the OrderView. This view should allow users to pick items from a list of products, add them to the actual order, choose quantities, view the current total price and submit the order. Also the user should be able to filter, sort and do pagination on the product list. I’m sure you’ve seen enough blog posts from other people talking about this subject, that is getting the selected product, add it to another collection or create an order detail based on it, then update some other data on the UI and finally submit the changes back to the server. The thing is, everyone has its own way for programming and eventually when you end up in a team, you may find that two people coded the same thing in a different way, and one has a bug in situation A and the other has a bug in situation B. Having a good MVVM framework with a well defined methodology is a must to prevent these situations. In this post I want to talk about essential components you must have in an MVVM framework. Later, I’ll describe an MVVM Framework I’ve been working on which was based on WCF RIA Services but doesn’t really depend on it.

Since we’re following best practices, we know that using a good MVVM architecture we can come up with a solution whose fetching, filtering, sorting, paging logic is entirely separated from the view, allowing us to also have different views for the same view model. For example, we can start by using a DataGrid and a DataPager to display our items but later provide a new view that uses comboboxes to select the sort options, an album-like listbox to show the items and custom buttons for paging. Also, we should be able to separate all this logic from its actual data access logic to be able to use mock objects for our model and do unit tests for our viewmodels. That’s not an easy task but that’s what I want to achieve from now on.

Well, to start, .NET / Silverlight already offers us some classes and interfaces that are very handy for MVVM scenarios.

  • INotifyPropertyChanged – Used to raise an event when a property changes. WPF / Silverlight Binding framework use this interface to update the view when a property changes.
  • INotifyCollectionChanged – Used to raise an event when an insert, remove, clear or replace operation has been done against a collection. WPF / Silverlight controls that have an ItemsSource property usually use this interface to create or delete visual items in a container. For example, ListBoxes display new ListBoxItems, DataGrids display new DataGridRows.
  • ICollectionView – Used to provide filter, sort descriptions, group descriptions, and item selection for an IEnumerable collection and have the view display only the filtered items, sorted according to the sort descriptions and highlight the selected item. (Has more features but these are the most relevant for the sake of this post).
  • IPagedCollectionView – Used to provide paging options to an IEnumerable collection. This is used by DataPagers mostly, that make calls to the MoveToPage(int pageIndex) method and allows us to register in the PageChanging event and fetch a new page of entities to be displayed.
  • There are other important interfaces like IEditableObject, IEditableCollectionView but I’m not going to cover those in this posts. They are used to update property values of an object in an atomic fashion.

Unfortunately, WCF RIA Services collections do not implement ICollectionView or IPagedCollectionView so we have to provide our own mechanism for filtering, sorting and paging data. However, this is fairly easy to do with LINQ and Expressions and we can compose queries with some LINQ operators with WCF RIA Services. Silverlight has its own implementation of IPagedCollectionView, called System.Windows.Data.PagedCollectionView but it was designed to work with in-memory collections and not server-side “collections” (data sources). In this series I’m providing my own IPagedCollectionView/ICollectionView implementation which I’ve designed to work with any kind of data sources. Whatever option is used is entirely up to the Model in MVVM.

So lets see how my MVVM Framework works.

First of, we have the model concept:

public interface IDomainModel : IDisposable
{
    void RejectChanges();
    ISubmitOperation SaveChanges();
    ISubmitOperation SaveChanges(Action<ISubmitOperation> callback, object userState);

    IDomainQuery<T> GetQuery<T>();

    void Add<T>(T entity);
    void Remove<T>(T entity);
    void Attach<T>(T entity);
    void Detach<T>(T entity);

    bool HasChanges { get; }
    bool IsSubmitting { get; }
    bool IsLoading { get; }
}

If you’re familiar with the DomainContext API, you’ll find this interface very easy to understand. This model interface is supposed to provide you with basic functionality for retrieving data, change it and save it. For more specific operations, you can extend this interface, add specific operations and have your viewmodel depend on it.

The IDomainQuery interface represents a query against a certain entity type repository (whatever that may be). As a query, it allows you to filter, sort and page the data.

public interface IDomainQuery : IDisposable
{
    void Cancel();
    void Load();

    event Action Loading;
    event Action<ILoadOperation> Loaded;
    event Action Canceled;
}

public interface IDomainQuery<T> : IDomainQuery
{
    /// <summary>
    /// Delegate that returns a lambda expression to apply in a Where operation
    /// </summary>
    Func<Expression<Func<T, bool>>> FilterExpression { get; set; }

    /// <summary>
    /// SortDescription values that are applied in a OrderBy/ThenBy operation
    /// </summary>
    Func<SortDescriptionCollection> SortDescriptions { get; set; }

    /// <summary>
    /// Contains the required data that are applied in a Skip and Take operation
    /// </summary>
    Func<PageDescription> PageDescription { get; set; }

    new event Action<ILoadOperation<T>> Loaded;
}

public sealed class PageDescription
{
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
}

Besides, it has events to let you know when it is has begun and completed the load operation. There are two important things to note in the IDomainQuery interface. First is the fact that the way you filter an IDomainQuery is how you filter a RIA Services EntityQuery and how you filter any IQueryable object. That means it’s very easy to do IDomainQuery implementations that query in-memory repositories or server-side repositories (note that EntityQuery doesn’t implement IQueryable and it’s not easy to integrate both concepts). The other important thing is that the filter, sort, page mechanism is based on delegates which means that whenever the query is executed/reexecuted these delegates are always reevaluated and will always reflect your viewmodel’s current state.

Last but not least, we have the operations. I’ve created interfaces for Load and Submit operations. I’ve implemented all these interfaces with classes that rely on WCF RIA Services. I’m not going to post the code of those classes but they’re available in the source code repository so you can take a look.

If you’re familiar with DomainContext your’re probably wondering where are the EntitySets and where do you manipulate collections. Well, since my IDomainModel implementation relies on WCF RIA Services, you can extend that class and manipulate your DomainContext to implement any operation that you want to make available in your extended IDomainModel. But with this framework they’re not supposed to be accessed in your viewmodel. Instead, I use my ICollectionView implementation, which I called DomainCollectionView.

public interface IDomainCollectionView : ICollectionView, IPagedCollectionView, INotifyPropertyChanged
{
    void Add(object item);
    void Remove(object item);
}

public interface IDomainCollectionView<T> : IDomainCollectionView, IEnumerable<T>
{
    void Add(T item);
    void Remove(T item);
}


The actual implementation relies on an IDomainQuery and will be a view for the results that are fetched in every operation. Since you can implement an IDomainQuery for any IEnumerable you can use this implementation for any data source.

public class DomainCollectionView<T> : ViewModelBase, IDomainCollectionView<T>
{
    private IDomainQuery<T> query;
    private ObservableCollection<T> sourceCollection;

    #region DomainCollectionView Members

    public DomainCollectionView(IDomainQuery<T> query, bool usePaging = true)
    {
        this.query = query;
        this.sourceCollection = this.CreateSourceCollection();
        this.CurrentItem = null;
        this.CurrentPosition = -1;

        //Apply sort and paging
        query.SortDescriptions = () => this.SortDescriptions;
        if (usePaging)
        {
            this.canChangePage = true;
            this.pageSize = 10;// default page size to 10
            query.PageDescription = () => new PageDescription { PageSize = this.PageSize, PageIndex = this.PageIndex };
        }

        query.Loaded += this.OnDataRefreshed;
    }

    private void OnDataRefreshed(ILoadOperation<T> loadOp)
    {
        using (this.DeferRefresh())
        {
            this.sourceCollection.Clear();
            foreach (var entity in loadOp.Entities)
                this.sourceCollection.Add(entity);

            this.ItemCount = loadOp.TotalEntityCount;
            this.TotalItemCount = loadOp.TotalEntityCount;
        }
    }

    //...
}

Now that I’ve introduced the model and the IDomainCollectionView, it is time we talk a bit about the viewmodel.

Unlike the model, viewmodels aren’t much reusable because they usually depend on the view’s needs. However, a lot of views in LOB apps have common needs: searching/listing entities, editing entities, create associations between entities. We can create viewmodels for these concepts and extend them only to implement business logic. All the infrastructure and plumbing can be centralized and reused everywhere.

In the next post I want to talk about how we can create base viewmodels for these concepts. But before we finish, I’ll leave just an initial thought on a base viewmodel that can be used to search/list entities.

public class ListVM<T> : ViewModelBase, IListVM<T>
{
    private IDomainModel model;

    public ListVM() { }

    protected virtual IDomainModel CreateModel()
    {
        return ServiceLocator.Current.GetInstance<IDomainModel>();
    }

    public void Initialize(IListVMInitializeArgs args)
    {
        this.model = args.ExistingModel ?? this.CreateModel();
        var query = model.GetQuery<T>();
        this.Entities = new DomainCollectionView<T>(query, args.RequiresPaging);
        query.Load();
    }

    public IDomainCollectionView<T> Entities
    {
        get;
        private set;
    }
}

With just a few lines of code, we have a base viewmodel that can:

  • Display results from a query that can be sorted and paged on the server. Just bind a datagrid to the Entities collection and play with it.
  • Reuse an existing model or create a new one. The current implementation can use a shared model for the whole app that is registered in the current Dependency Injection container or let a derived viewmodel decide what to do.

I’ll let you think about it. In the next post I’ll dive deeper and add a lot more functionality in these viewmodels.

See you soon,

Happy coding.

Advertisements

8 thoughts on “Architecting Silverlight LOB applications (Part 6) – Building an MVVM Framework

  1. Hi Manuel

    When do you think will you continue with the very interessting serie ‘Architecting Silverlight LOB applications’?

    Regards
    Björn.

    • Hello Bjorn,

      Thank you for your interest on the series.

      i have plans to continue soon. i haven’t writen anything lately due to 2 reasons: lack of time and broken keyboard.. i can’t type ‘i’ or ‘;’ on my keyboard.. i have to put those keys on clipboard and copy-paste all the time. That makes programming a pain in the a**.

      This problem has been going on since december and i needed my computer to write my masters thesis. Good news is that i’m going to buy today a new laptop and send my current one to ASUS support for a new keyboard (off-warranty).

      Another good new for this blog is that i’m seriously thinking in dropping my master thesis and do a different one next year. This is because i’ve been writting about algorithms for Queue Management Systems and honestly i don’t like this topic that much.. i should have writen about software architecture, which is what i intend to do next year.

  2. Hi Manuel. good luck with your master, any of them :).
    I´m currently implementing a framework at work, using EF-ria. As support for MVVM we are considering using
    caliburn.micro.(coroutines and binding by convention , viewmodel first )
    Have you had a look at that framework ?. if so, any comments on pros and cons ?.

    Rgs Per H.

  3. Hi,

    Very good and useful series. Eagerly Waiting for the next blog post. Any timeline ? Went to the download source code link but there is code till 5th episode.

    there are some aspects on which I would like your suggestions too.

    1) How to handle popups ? I mean say user need to edit an entity and the edit/add forms should be a popup and a reusable one ?

    2) You mentioned that one may extend the BaseRepository as needed and there is stub project in your solution “MyApp.Domain.Core” project. Can you give some examples? It would be really helpful to a beginner like me.

    3) I like the fact that each modules will provide its own menus. Is there any other approach ?

    4) I am about to read the 6th episode and I saw code realted to paging and filtering. What is the best way according to you for a large data centric application. I am sure you would be aware of flex and they provide a great way to load large datasets async. Any pointers on that problem space in dotnet world?

    Again, Thanks much for the wonderful series. \

    Mrunal Buch

    • Hi, sorry for the late reply.

      There’s been more than a year that I lost track of this series, mainly due to lack of time but also lack of interest. I’ll make a new post about the reasons why I haven’t continued this series.

      Anyway, I’m glad to help in whatever questions you may have.

      1) I would use the Messenger component and create a new message, lets call it EditTaskMessage. That message could have a callback delegate property that you can use to pass the entity after the user has edited/added it in the popup. This means you would need to subscribe the EditTaskMessage, create the appropriate popup and a viewmodel, and when the edit/add is done, close the popup and invoke the callback along with the entity.

      2) The BaseRepository provides methods for the basic CRUD scenarios. If you need specific data access operations, like ListUsersByCompany you can create an IUserRepository and an implementation that hides the query details. Anyway, if you don’t mind having your queries being made in other layers, you can always use an Expression<Func> that can be passed to a LINQ .Where operator.

      3) There are many approaches.. but that depends on the complexity and requirements of your application.. you could have the menu configured in a database or xml file in the server and when a module loads you can download the menu commands for that module…

      4) As long as you do paginated queries, all you need to worry about scalability is server-side. A nice way to do paging is scrolling down (like mobile apps usually do). I think Telerik has controls that handle when the user scrolls and when you need to fetch more data. You can wire those datagrid events to your viewmodel’s paging logic and everything should be fine.
      I used Telerik’s radcontrols in my previous Silverlight project but I never got the chance to use this feature.

      If you have any other questions, feel free to ask 🙂

  4. Anything further on the series… or any more architecture advice? I was really looking forward to the next post and source code….

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s