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

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

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

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.

 

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

  • EyadAb

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


    posted by EyadAb on Dec 26, 2011 13:07

    Hi, thanks you for sharing these nice startegies.

    Currently I am working in a project that look like multi-window MDI using RadWindows. I am impelemnting the first strategy, one domainContext per viewmodel, since I need to save data only in that correspoing form.

    But I have some questions please:
    1- If am keeping the domainContext sync through realod, the MergeCurrent LoadBehavior would be the best right!
    2- Could you share some ideas about the impact of saving data using Invoke rather than submitchanges.
    (In a form, although there is a validation error in some entites I need to be able to delete other entites), Currently am detaching the records from the domain context after DeleteInvoke executes successfully.
    3- Could you explain more:  "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. "

    Thanks and regards

  • ShohelShipon

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


    posted by ShohelShipon on Jul 29, 2012 07:50

    Nice 

Add Comment

Login to comment:
  *      *       

From this series