(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Silverlight WCF RIA Services: Strategies for handling your Domain Context - Part 1

(6 votes)
Kevin Dockx
>
Kevin Dockx
Joined Nov 15, 2010
Articles:   19
Comments:   8
More Articles
6 comments   /   posted on Mar 17, 2011

This is the first in a two-part article series on the WCF RIA Services Domain Context.

This article series is accompanied by source code, which can be downloaded here.

Introduction

A lot of business applications that are being developed in Silverlight today are built with the help of WCF RIA Services. This should come as no surprise, as it’s a really powerful, extensible framework which provides us with a lot of features out of the box (validation, authentication, authorization, …) that otherwise would require quite a lot of custom code, workarounds & plumbing. In WCF RIA Services, you’re going to be working with a client-side Domain Context instance. This article will look into a few strategies on working with this Domain Context, and is accompanied by a demo & source code, which you can download here.

But let’s start with a short introduction on what a Domain Context actually is.

What is a Domain Context?

When you create a new Domain Service & build your project, you’ll notice a Domain Context class (a class inheriting DomainContext) has been generated for you on the client (in a Silverlight class library project if you’re using a WCF RIA Services class library, or straight into your Silverlight project): one for each Domain Service. If you create a Domain Service named MyDomainService, you’ll get a generated Domain Context on your client named MyDomainContext, which inherits from the DomainContext class. It contains query operations to fetch data, collections of the entities you’re exposing through the Domain Service, submit and reject operations, … It provides a lot of functionality to help you with change tracking & interacting with the underlying Domain Service.

Those of you who are familiar with an object relation mapper (ORM), like the Entity Framework, will feel right at home: working with the WCF RIA Services Domain Context is quite similar to working with the Object Context you get when using the Entity Framework: you load data into the Context, the Context has collections of Entities you’re exposing through your Domain Service(s), it allows for change tracking on these entities, and you get your typical submit & reject-operations. Of course, you’re working in an asynchronous, service oriented environment when using WCF RIA Services: what actually happens when you submit the changes on your Domain Context is that that same context is rebuilt on the server side, and submitted once completely built.

As an example, we’ll assume we’ve got an Entity Model with a Category & Book object (cfr John Papa’s Bookstore Club example). A Book belongs to a Category, so one Category can have multiple Book objects: we’ve got a Navigation Property Books on our Category. When you create a Domain Service, BookDomainService, and select these 2 entities, WCF RIA Services will generate all the CRUD operations for you.  Note that this “generation” is done once; it’s nothing more than a bit of help to get you started (however: in real-life scenarios, take care of this: you do not want operations to be exposed through your services which aren’t used) – this is different from the generation of the client-side Domain Context, which happens on each build. 

 

clip_image001

In your Silverlight application, you’ve now got a BookDomainContext. This Domain Context contains a list of Books and a list of Categories. Each Category from that list has a list, Books (eg: the NavigationProperty from the EntityModel).

Image we load all the categories & their books. We change 1 book, add 1 category and add 1 new book to that category. When you submit these changes, the UpdateBook method will get executed once, the InsertCategory & InsertBook methods will get executed once as well, and only after that, the server-side Object Context is submitted, resulting in the necessary queries to your DB. You can easily check this behavior by overriding the Submit method on your Domain Service: if you set a breakpoint on it, you’ll notice it will only get hit after the CUD operations have been executed.

So, in essence, what we’ve got is a client-side version of a server-side Object Context. Needless to say: this approach became very popular in a short amount of time, and WCF RIA Services is being used for large, line of business applications all over the world.

However: when you choose this approach, you should be aware of how the DomainContext works and what the different strategies for using it are. The explanation above should already give you an idea of its inner workings, but when starting a new project, an important question will always rise up: should I use one or more instances of my Domain Context(s)?

Instance strategies for your Domain Context

In essence, you’ve got to choose between 3 different approaches: working with one general Domain Context instance, shared across all your ViewModels, working with a specific Domain Context instance on each ViewModel, or a mix of the previous 2 approaches, where some ViewModels share the same Domain Context instance, and others have their own. We will now look into these approaches, list a few reasons to choose (or reject) a certain approach, and look into the issues that you might face with each approach.

First things first though: regardless of the chosen approach, it’s always a good idea to think about what should go in each Domain Service. Just a few months ago, we didn’t have a lot of choice: using the same entity in two different Domain Services wasn’t supported and resulted in an error. Since SP1, this has been solved: you can now easily share the same entity in different Domain Services (and thus have it tracked by different Domain Contexts). So that would be the first tip: split up your domain services depending on the functionality a certain module of your application requires. This not only keeps them much easier to maintain, it also allows for greater abstraction & code re-use.

Strategy 1: one Domain Context instance for each ViewModel

Choosing the right approach will typically depend on the (functional) requirements of your application. First of all, we’ll look into the “one Domain Context instance for each ViewModel” approach. This approach is used in a lot of examples you can find online, and it looks as the right way to go for a lot of applications.

The advantages aren’t hard to see: as a Domain Context cannot do partial submits, a submit operation on your context will submit all the changed entities on that context at once. One instance per ViewModel allows you to easily submit only the changes that have been made to your data used in that ViewModel. Next to that, if you’ve got different VM’s working on different sets of data of the same type, you can keep on working straight on the EntitySets (or CollectionViews on those entity sets) of your Domain Context instance. For example: in one ViewModel you could load all new Books in the Book collection on your Domain Context instance, in another ViewModel you could load all Books from a certain author in that collection on your Domain Context (of course, there are other ways to achieve the same – for example, working with ICollectionViews and filtering the data on the client or server, using filters on your EntityQuery, …). The essence is: your Domain Context isn’t shared, so it’s kept within the boundaries of that ViewModel: whatever you do on it, it’s not reflected in other ViewModels.

This is the approach we took when creating our 360 Review Application, of which I wrote a white paper a few weeks ago. Our main concerns here were: allow the user to navigate wherever he wants without requiring him to save changes before leaving an active screen (eg: a user could easily have 4, 5 active screens with unsubmitted changes), and only submit the data that is needed to the server per screen, not for all screens at once.

However, this does pose a problem: keeping your Domain Contexts in sync across different ViewModels. For example, imagine two parts of an application, two different ViewModels working on the same (or rather, related) data. A simple example could be: in one ViewModel, for example the one used in a sort of Dashboard screen in your application, you get an overview of all the Books in your DB. In another ViewModel, you’re allowing the user to add, edit, delete, … one of those books. Image you’re adding a new Book, submit the changes to server, and go back to the other view: the new Book won’t show up there, as, at the moment, it’s not being tracked by that DomainContext instance. You could of course automatically refetch the Employee collection whenever a user navigates to the dashboard View (and underlying ViewModel), but that’s not too good, performance and bandwidth wise.

How can we handle this? Well, out of the box, you can’t: there’s no way to automatically “sync” different Domain Context instances. It will require some custom code, and how do this, again, depends on your application requirements.

I’ve created an example application to show this behavior (note: the example applications uses Laurent Bugnions’ MVVM Light Toolkit - powerful & really lightweight, check it out if you haven’t done so yet, and John Papa’s Bookstore Club database).

In this application, you can see 2 views on the same page, of which the ViewModels have their own Domain Context instance. Both Views show a list of all the books in the database – they are bound to an EntitySet<Book> (Context.Books), which is loaded in the VM’s constructor. When you add a Book on the second view, a new Book (with some default values filled out) is added to the Domain Context, and the Domain Context is saved by calling its SubmitChanges() method. You’ll notice that the book is correctly added to the end of the book ListBox on the right, but it’s missing in the ListBox on the left.

So, how do we make sure both Domain Contexts are kept in sync? One possible approach would be to manually redo the changes you’ve made on one instance on the other. As you might know, entities can be attached to a context in code: what you can do is send a message when the book has been submitted, containing the entity you’ve just added, to a ViewModel that’s handling entities of the same type. The receiving ViewModel would then attach the Entity you pass in to its Context, resulting in 2 synced Domain Context instances. You do need to create a copy of the Entity to attach, as an Entity can only be tracked by one Domain Context instance at the same time. In our example, the code would look like this:

Submit the book in the ViewModel on the right, and create a new message:

private void AddBookAndAttachExecution()
{
    // add the book to the context, and save changes
    Context.Books.Add(NewBook);
    var submitOp = Context.SubmitChanges();
    submitOp.Completed += (send, args) =>
         {
             if (submitOp.HasError == false)
             {
                 // send a message telling any registered VM's a book has been added
                   Messenger.Default.Send<BookAddedMessage>(
                     new BookAddedMessage(NewBook));
                 // create a new book with default values
                 NewBook = new Book()
                 {
                     ASIN = "123",
                     AddedDate = DateTime.Now,
                     Description = "Default description",
                     Author = "Author",
                     Title = "Title",
                     PublishDate = DateTime.Now,
                     CategoryID = 1,
                     MemberID = 1
                 };
             }
         };
}
 

The message constructor creates a copy of the Book:

public class BookAddedMessage
{
    public Book Book;
    public BookAddedMessage(Book book)
    {
        // create a COPY of the book that's passed in. This one
        // is attached to another context, and you cannot attach the
        // same entity to two domain context instances.
        this.Book = new Book()
             {
                 BookID = book.BookID,
                 ASIN = book.ASIN,
                 AddedDate = book.AddedDate,
                 Description = book.Description,
                 Author = book.Author,
                 Title = book.Title,
                 PublishDate = book.PublishDate,
                 CategoryID = book.CategoryID,
                 MemberID = book.MemberID
             };
    }
}

The message recipient, in our example: the ViewModel on the left, attaches the Book to its context:

private void BookAddedMessageReceived(BookAddedMessage msg)
{
    // attach the newly added book (in an unmodified state)
    Context.Books.Attach(msg.Book);
}

This results in the following (Domain Context instances are in sync):

dc2

The advantage of this approach is that it doesn’t require an extra load operation to refetch the list of books from the server – the Book is submitted once, and all the rest is handled on the client (you might want to write an extension method to copy Entities to make your life easier). However, you’ll quickly run into the fact that designing your application like this will require a lot of maintenance: in this example, we’re only talking about 1 list of books, but in a real-life application, you’ll typically be handling a multitude of possible objects & child objects, which could be added, updated or removed, on a multitude of ViewModels with a multitude of Domain Context instances.

Another possible approach is to send messages to the related ViewModels, telling them that they have to refresh themselves. A refresh would re-load the data from the server, in this case: reload the list of books. In the example application, that would look like this:

Submit the book in the ViewModel on the right, and create a new message:

private void AddBookAndRefreshExecution()
{
    // add the book to the context, and save changes
    Context.Books.Add(NewBook);
    var submitOp = Context.SubmitChanges();
    submitOp.Completed += (send, args) =>
    {
        if (submitOp.HasError == false)
        {
            // send a message telling any registered VM's to refresh
            Messenger.Default.Send<RefreshBooksMessage>(
              new RefreshBooksMessage());
            // create a new book with default values
            NewBook = new Book()
            {
                ASIN = "123",
                AddedDate = DateTime.Now,
                Description = "Default description",
                Author = "Author",
                Title = "Title",
                PublishDate = DateTime.Now,
                CategoryID = 1,
                MemberID = 1
            };
        }
    };
}

The message recipient, in our example: the ViewModel on the left, refetches the list of books:

private void RefreshBooksMessageReceived(RefreshBooksMessage msg)
{
    // reload the data
    LoadData();
}
private void LoadData()
{
    Context.Load<Book>(Context.GetBooksQuery());
}

The disadvantage of this approach is that it comes with higher bandwidth usage: you’re refetching data from the server. But the advantage comes in the form of simplicity and lower maintenance: instead of having to pass through all the changed objects & child objects, you simply send one message: refresh entities of this type. Depending on how you wrote your EntityQueries, the child objects (when applicable) will be refetched if they need to be.

That’s all for the first strategy.  Stay put for the second and third approach in the next part of this article series!

 

About the author

Kevin Dockx lives in Belgium and works at RealDolmen, one of Belgium's biggest ICT companies, where he is a technical specialist/project leader on .NET web applications, mainly Silverlight, and a solution manager for Rich Applications (Silverlight, Windows Phone 7 Series, WPF, Surface). His main focus lies on all things Silverlight, but he still keeps an eye on the new developments concerning other products from the Microsoft .NET (Web) Stack. As a Silverlight enthusiast, he's a regular speaker on various national and international events, like Microsoft DevDays in The Netherlands, Microsoft Techdays in Portugal and Belgium, or on BESUG events (the Belgian Silverlight User Group). Next to that, he also authored a best-selling Silverlight book, Packt Publishing's Silverlight 4 Data and Services Cookbook, together with Gill Cleeren. His blog, which contains various tidbits on Silverlight, .NET, and the occasional rambling, can be found at http://blog.kevindockx.com/, and of course he can also be found on Twitter @KevinDockx


Subscribe

Comments

  • -_-

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by Adam on Mar 17, 2011 16:59
    I look forward to the next!  I had faced this exact issue with my last project.  I went with your first approach from this page, multiple contexts. I also reload the data between screens too however (no sync).  Halfway into the app I realized that if I had a single context I'd not need to do this, and could have a master save button.  The user experience would have been better but I would have had to add code to navigate back to specific pages with errors if they came up during save.   With the approach you listed here, the user is on the page with the data necessary to deal with any errors generated when they save. (At least in my case)
  • -_-

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by Sølve on Mar 17, 2011 22:16
    Thanks for a very well written part one, I'm really looking forward to part two.
    PS: I'm planning to use more sharing in my current project. 
  • -_-

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by Matt on Mar 18, 2011 18:51

    I am curious about the affect on performance and memory consumption using Strategy 1.  It seems like having a new instance of the Domain context in every viewmodel could cause duplicate copies of data througout the application and increase the CPU usage. 

    For instance if I have aViewModel that has a collection with hundreds of items in it, and other views that need that same collection, is there a good way using Strategy 1 to share data across ViewModels, or across Domain Contexts so you don't have to load 2 copies of the same collection in different pages.

  • KevinDockx

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by KevinDockx on Mar 21, 2011 11:21

    Adam, Solve: glad you liked the article / to have been of help :-)

    Matt: with this approach, you would indeed get multiple copies of "the same" entities (possibly: one for each domain context instance).  An entity can only be tracked by one domain context instance, so these "copies" must be made - if you're using the "multiple domain context instances" approach, you will have to have an entity instance of each entity for each domain context (if the entities are to be used by both).  If you do not want these copies, you might want to take a look at the second part of this article series.  

    Technically speaking, these aren't exact copies: you're making a copy to start from (just to keep the contexts in sync), but after that, the entities could easily change between VM's/context instances. Eg: an advantage could be the case where the "same" entity has client side properties that are, and should be, different between ViewModels.

    As usual, the "best" approach will depend on the requirements of your app.

  • -_-

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by rknell on Apr 12, 2011 04:26
    Part 2 and 3 please please please!!! I currently have a thread running on the silverlight forum about this exact issue. I went with the global domain context, but since you cant perform a load operation while you have changes, I am constantly struggling with saving the context in time. Since I am trying to move my app to a tabbed browser like interface, with multiple copys of views loaded they need to have their own domain context - well view model actually, but I want to be able to save individual changes. Very excited to hear about option 3, thats something I have spent many days pondering
  • SilverlightShow

    RE: Silverlight & WCF RIA Services: Strategies for handling your Domain Context - Part 1


    posted by SilverlightShow on Apr 12, 2011 10:15

    Hi,

    Part 2 of this article demonstrating the second and third approach is available at http://www.silverlightshow.net/items/Silverlight-WCF-RIA-Services-strategies-for-handling-your-Domain-Context-part-two.aspx

    Have a nice reading :)

    SilverlightShow Team

Add Comment

Login to comment:
  *      *       

From this series