2013年12月23日星期一

Silverlight WCF RIA Services: Strategies for handling your Domain Context

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.


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

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!



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

This is the second 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.
In the first part, we went through a general introduction on what the WCF RIA Services Domain Context is, looked into different strategies on how to work with it, and had a detailed look at the first possible strategy: using one instance per ViewModel. In this article, we’ll look into the other strategies.

Strategy 2: using one shared Domain Context Instance

Let’s have a look at the other approach: using one shared Domain Context instance across different ViewModels. As you might have guessed, the pros and cons are more or less the opposite of the previous approach: the good part is that you get automatically synced collections across your ViewModels: as they’re using the same Domain Context instance, adding a Book in one ViewModel will mean it’s also reflected in the other ViewModel (well, as long as you’re working directly with the EntitySet or with one of the built-in CollectionViews – eg, anything that tracks the collections on the Domain Context).
This is the approach I tend to prefer for most intranet-applications we’re building these days. Keep in mind: you’ll still have multiple Domain Context instances because you should think about how to separate your operations in different Domain Services, however, that’s not the same as different instances of the same Domain Context.
In our example, I’ve created a static LocalStateContainer class, which is where the Domain Context instance resides. The ViewModels have a Context property which links back to the Domain Context instance in the LocalStateContainer class:
public static class LocalStateContainer
{
    private static BookDomainContext _context;
    public static BookDomainContext Context
    {
        get
        {
            if (_context == null)
            {
                _context = new BookDomainContext();
            }
            return _context;
        }
    }
}
This results in the following application screen:
dc3

If you look into the code, you’ll notice the ViewModel of the View on the left loads the data, and the one on the right doesn’t, yet: the data is available on both screens.
If you click the “Add both books” button on the View on your right, you’ll notice the Books that are submitted to the database are automatically reflected in both Views:
One (possibly big) disadvantage is the fact that a submit operation is “all or nothing”: you cannot do partial submits, so submitting changes will submit all changes across different screens. Depending on how you design your application, this may or may not be a problem: an easy way to solve this would be to disallow your users to navigate away from pages which have unsaved changes. In such a case, a submit op would still only submit the changes from the last page, as you can be sure that will be the only page with possible changes.
However, if you want to give your users a more open experience (multi-window MDI-like applications, or applications which allow the user to navigate back and forth between pages without having to save the changes), this could pose some problems: imagine changing an Entity in one part of your application, navigating to another part, changing another Entity and submitting the changes: what if the first Entity has some invalid values? You’ll end up with an error message which actually belongs to a page that isn’t active nor necessarily visible anymore to your user – not exactly user friendly. One way to solve this (an approach we used in one of our applications) could be to keep a list of “active screens” (much like a task bar) on your screen. If a submit operation fails, you could easily use that “task bar” to show your user which active screen has problems, so he can go and fix those before retrying the submit operation. But as you’ve probably guessed: this is not exactly trivial code.
So let’s look into another way to solve this issue: partial saves. A partial save means you’ll only submit a part of the changed Entities: only the once you want to submit. Like this, you can submit the changes on one ViewModel without having to save changes on one of the other ViewModels.
Wait a minute. I’ve been saying throughout this article that partial saves aren’t possible through a Domain Context, so how can this be done? Well, the workaround is easier than it might look. I’ve already talked about attaching an Entity to a Domain Context, which implies you can also detach an Entity. The reasoning here is quite simple: a partial save can be done by detaching the entities you want to save from your Domain Context (IF they are attached to it), creating a new Domain Context instance (which will only be used for the submit operation), attaching them to that new instance in the correct state, submitting the changes, and on the completion of the submit operation, re-attach the submitted entities to the original Domain Context.
At least, that’s how it works in theory. In practice, you’ll find out that the part where you should attach entities in the correct state is the tricky part: if you attach an Entity, it’s attached with state “Unmodified” by default, and you cannot easily change that state. For our example, the solution is quite easy: attaching it in the correct state boils down to adding the Book to the Books collection on the new Domain Context.
The code to achieve this looks as such:
private void AddFirstBookExecution()
{
    // for example purposes, we'll first add the two
    // books to the original context.

    Context.Books.Add(NewBook);
    Context.Books.Add(SecondNewBook);
    
    // create a new context instance
    BookDomainContext submitContext = new BookDomainContext();

    // detach the book
    Context.Books.Detach(NewBook);

    // attach the book to the new context in the correct state,
    // eg: add it.
    submitContext.Books.Add(NewBook);

    // submit the new context
    var submitOp = submitContext.SubmitChanges();
    submitOp.Completed += (send, args) =>
    {
        if (submitOp.HasError == false)
        {
            
            // detach
            Book submittedBook = (Book)submitOp.ChangeSet.AddedEntities[0];
            submitContext.Books.Detach(submittedBook);

            // attach the book back to the old context
            Context.Books.Attach(submittedBook);

            // inspecting the old context through
            // Context.HasChanges => will be true: the 
            // SecondNewBook will not be submitted.
        }
    };
}
But what about modified entities, or deleted entities? Essentially, you’re going to have to write custom code to handle these cases. Luckily, a great starting point for these cases is WCF RIA Services Contrib, by Colin Blair. This project consists of a bunch of extensions on WCF RIA Services, one of which allows you to achieve partial saves through the Entity.ApplyState-method.

Strategy 3: a mix of 1 & 2?

The third approach is a mix of the approaches I’ve described above: the same pros and cons apply, but instead of having no cons at all, you might end up with all of them in the same project… In general, I’m not such a fan of this – often, this simply results in having to have all possible workarounds in one project.


Conclusion

The Domain Context is really powerful, but you should be aware of how it works, and what the different strategies are for working with it. Each strategy has its own advantages and disadvantages, and workarounds for those disadvantages. The right strategy will highly depend on your project. In general, I would use the “one Domain Context instance per ViewModel” in cases where you absolutely need to be able to submit only the changes that are made on one ViewModel, and I’d use the “one shared Domain Context for all ViewModels” approach in cases where keeping the data automatically synced is important. From experience however, I find that most projects use the shared Domain Context approach.

Oh, and don’t forget: if you model your domain services right (eg: don’t design one Domain Service to track all your entities), you just might avoid a lot of problems, so that’s always the first thing to do.