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

Building N-Tier business applications with WCF RIA Services – Part II

(9 votes)
Zoltan Arvai
>
Zoltan Arvai
Joined Jul 20, 2009
Articles:   7
Comments:   5
More Articles
12 comments   /   posted on Dec 07, 2009
Categories:   Data Access , Line-of-Business

This article is compatible with the latest version of Silverlight.

Introduction

The last few weeks have been very exciting. At PDC 2009 Microsoft announced WCF RIA Services which is now the official name of .NET RIA Services. Their intention is to emphasize how close RIA Services and WCF is. However there were minor changes to the API it didn’t affect my previous article so you can still consider it valid.

In my last article I wrote about the basic concepts of WCF RIA Services and n-tier development. In this one we’ll talk about the features of WCF RIA Services in terms of creating complex queries.

Setting up WCF RIA Services

We know how to create simple queries against a custom layer. Now I’m going to use Entity Framework and I’ll create an Entity Data Model (EDM) for Northwind database using Visual Studio wizards. My EDM is very simple I’m using only two tables.

entity

WCF RIA Services has a Visual Studio template and a related wizard which will help me generate my Domain Service class. I added a new item called Domain Service Class. Now Visual Studio pops up a dialog like this one:

 

image

Notice the different checkboxes. I enabled client access for this DomainService which will put the EnableClientAccessAttribute on top of my DomainService class. This will trigger a code generation process, and a client-side DomainContext class will be generated. Now my data access layer is Entity Framework provided, so as an ObjectContext I selected NorthwindEntities, and I also enabled both my entities to be published over RIA Services. By checking the last checkbox I generated metadata classes for my domain entities. (I’ll explain the roles of metadata classes later in the article.)

The following code is generated for me:

[EnableClientAccess()]
   public class NorthwindDomainService : LinqToEntitiesDomainService<NorthwindEntities>
   {
       public IQueryable<Categories> GetCategories()
       {
           return this.ObjectContext.Categories;
       }
 
       public IQueryable<Products> GetProducts()
       {
           return this.Context.Products;
       }
   }

 

This time the base class of my NorthwindDomainService is not DomainService, it is LinqToEntitiesDomainService<T> which is basically a special domain service base class for Entity Framework. It publishes a ObjectContext property which provides you direct access the your ObjectContext instance. So you can write your query against this property.

After a simple build process code generation happens. Client-side objects are now created, so all we have to do now is to start writing queries.

Simple Query Model

My first query is simple. Get me all the products which belong to the ”Beverages” category:

NorthwindDomainContext ctx = new NorthwindDomainContext();
            
var query = from p in ctx.GetProductsQuery()
            where p.Categories.CategoryName == "Beverages"
            select p;
 
ctx.Load<Products>(query);
 
productDataGrid.ItemsSource = ctx.Products;

So my client-side context is my DomainContext instance which provides me all the features WCF RIA Services offers, like transparent asynchronous communication to the DomainService, client-side change tracking, etc...

To compose a query I have to use the GetProductsQuery method which returns an EntityQuery and uses the server side GetProducts method to serve as the base of the query. See how easy it was to navigate through associations? We don’t have this data (Categories.CategoryName) on the client side, my query has to be run on the server-side.

The Load() method triggers the actual call to the domain service. It’s an asynchronous operation. The Result will be loaded into the Products EntitySet.

clip_image002

See how easy that was? It was like writing a query directly against my EDM. But what if something bad happens, maybe an exception occurs. Our call to the domain service is asynchronous so try-catch won’t help me much.

Collecting information about our call

The return type of the Load method is a LoadOperation<T> which has many usefull properties to determine what happened during the call.

Most important properties of LoadOperation<T> class:

  • HasError – Something bad happened during the call
  • Error – Wraps the exception that may occurred during the call
  • UserState – An object that can be passed by the caller for later use
  • Entities – List of all the loaded entities
  • EntityQuery – The composed query
  • IsCompleted – Is the operation complete?
  • IsCancelled – Is the operation cancelled?
  • CanCancel – Is the process in a state where it can be cancelled?

One way you could go is to poll your LoadOperation object whether it „IsCompleted” or not. Another way I prefer much more is to use callback functions. Let the domain context tell us once the operation is done. One of the overloads of Load() method accepts an Action<LoadOperation<T>> delegate.

private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
 
    NorthwindDomainContext ctx = new NorthwindDomainContext();
    
    var query = from p in ctx.GetProductsQuery()
                where p.Categories.CategoryName == "Beverages"
                select p;
 
    ctx.Load<Products>(query,new Action<LoadOperation<Products>>(product_LoadCompleted), null);
 
    productDataGrid.ItemsSource = ctx.Products;
}
 
private void product_LoadCompleted(LoadOperation<Products> operation)
{
    if (operation.HasError)
    {
        ErrorWindow.CreateNew(operation.Error);
        
    }
}

Now if something bad happens during the call (like we mistype the connection string on the server :) ) we can provide feedback:

clip_image002[5]

Controlling data flow

Once we have our data on the client-side we can start working with it. But what if after I loaded Categories into my DomainContext I need the related products as well. It can’t be already on the client-side. If it would be there it would mean that probably all the entities I publish through WCF RIA Services are there too, which in some cases means that my whole database is on the client side. This unacceptable, so Microsoft choose not to include related entities when sending back data to the client. Fortunately I have the option to tell RIA Services that every time I ask for a specific entity give me back the associated entities I want as well. This is done through metadata classes.

The easiest way to tell something about a property or a class is done through attributes. But once you have your data model generated you cannot change the code. You can write additional code to it since your classes are probably partial classes but you can’t add an extra attribute to a generated property because a regeneration process would destroy all your changes. This is where metadata classes come into play.

Metadata classes are internal classes defined in the class for which you want to provide extra information. In these classes you specify the properties you want to decorate and apply attributes to them. You tell the parent class to use this metadata class through the MetadataType attribute. Now WCF RIA Services knows when you work with your class you’ll want special treatment based on your attributes in the metadata class.

In our case we want to tell RIA Services to include the related Products list every time a category is being loaded. Remember when we checked ”Generated associated metadata classes”? It created a NorthwindDomainService.metadata.cs file for us. Let’s see what’s inside:

[MetadataTypeAttribute(typeof(Categories.CategoriesMetadata))]
public partial class Categories
{
    internal sealed class CategoriesMetadata
    {
        private CategoriesMetadata()
        {
        }
 
        public int CategoryID;
 
        public string CategoryName;
 
        public string Description;
 
        public EntityState EntityState;
 
        public byte[] Picture;
 
        public EntityCollection<Products> Products;
    }
}

You can see that the Categories class is associated with the CategoriesMetadata class using the MetadataType attribute. Now what I have to do is to decorate the Products field in the CategoriesMetadata class and tell RIA Services to include this association into every query.

[Include]
public EntityCollection<Products> Products;

That’s it. Of course you’ll have to tell you data access layer as well to load the associated data from the database. In this case:

public IQueryable<Categories> GetCategories()
{
    return this.Context.Categories.Include("Products");
}
However this works you cannot determine on the client side whether you want to include the list of products or not. What you can do to solve this issue is to modify the GetCategories domain operation like this:
public IQueryable<Categories> GetCategories(bool loadProducts)
{
    if (loadProducts)
    {
        return this.Context.Categories.Include("Products");
    }
    else return this.ObjectContext.Categories;
}

Now you can pass a boolean value to determine whether you need the products as well or not.

The client side code will look like the following:

NorthwindDomainContext ctx = new NorthwindDomainContext();
 
//Pass true to load the realted products, false not to.
var query = ctx.GetCategoriesQuery(true);
 
ctx.Load<Categories>(query,
    new Action<LoadOperation<Categories>>(categories_LoadCompleted), null);

 

Summary

As you’ve seen WCF RIA Services provides you a great deal of flexibility when writing queries and also provides different means to control and gather information about a specific call. When querying against a domain service we should not forget that associated entities might not be loaded as well. To solve this issue we can use metadata classes to tell RIA Services what it should include in the response and what is shouldn’t. In the next part we’ll see how to modify data using WCF RIA Services. I hope this helps.


Subscribe

Comments

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by middlevn on Jan 28, 2010 05:32
    Thanks for the interesting post. I am using MySQL as backend database for my project. I wonder if there is any custom way to implement the so called "[Include]" so I can also load the related entities because,  you know, MySQL don not support foreign keys or specifically associations in the Entity Model using in RIA.

    Regards,
    Trung Hoang
  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Radu Poenaru on Feb 24, 2010 13:59

    Dear Trung Hoang,

    Do you use MyISAM tables in your MySQL database?

    I just created a MySQL database and default all new tables are created using MyISAM which, indeed, doesn't support Foreign keys.

    But if you switch all tables to use the InnoDB engine instead, you'll be able to create foreign keys and use them in your RIA app with Entity Model.

    I am in preparation of a article regarding RIA services, Silverlight and MySQL databases on my website, because I faced this issue on my own and I believe that it is very interesting to share this.

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Lou on Feb 25, 2010 02:18
    In this example Products are being included in the Category.
    What if the Category has a related EntityCollection.

    In my case I have Customers, Dogs and the Dogs have related vaccinations. I can select a customer and get the related dogs, however I can not get the related vaccinations, is there a way to get the data that is related to the related data?
  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by RAM on Apr 06, 2010 15:27
    Hi,

     

    I am new to .net and this posting was realy very informative , thankyou.

     

    got a dump question though, in your example

     

    NorthwindDomainContext ctx = new NorthwindDomainContext();
                
    var query = from p in ctx.GetProductsQuery()
                where p.Categories.CategoryName == "Beverages"
                select p;
     
    ctx.Load<Products>(query);
     
    productDataGrid.ItemsSource = ctx.Products;

     

    where does the filteration of data occurs server side or client side,

     

    does the service return complete product table data to client and client applies the filter or is .Net smart enough to send the query to the server and apply the filter on server itself.

    thanks

    ram 

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Zoltan Arvai on Apr 07, 2010 03:44

    Lou: You have to add the [Include] attribute on top of the property (association) in the metadata class. And you also have to load it from the database / datasource and pass the data back aslo to the client side.

    RAM: The short answer is yes, of course :)

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Andy on May 19, 2010 20:57

    Hi, I'm probably missing something here as I'm trying to understand the difference between a "conventional" WCF service as would be used in an ASP.Net app (pre-VS2010), and an RIA service, where domain services fit in, etc.

    In your article under the heading "Simple Query Model" you demonstrate a client-side LINQ query to retrieve products. Surely this isn't n-tier is it - i.e. executing business logic client side? I'm from an ASP.Net background and would normally develop a business tier (WCF service), exposing an operation such as "GetProductsForCategory(string categoryName)", that the client side would call. I'm a little confused as to why you would be executing a query on the client side.

    Thanks in advance

    Andy

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Mr. Pomo on Jun 03, 2010 01:33
    After looking at all the stringent requirements and after seeing all the codgen create by these projects, all i have to say is, this technology will never last. Thanks
  • zoltan.arvai

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by zoltan.arvai on Jun 16, 2010 15:21

    Hi Andy!

    I'm not really executing business logic on the client side... What I actually do is download data from the server and passing it to he silverlight client to present it on the UI. I think using linq queries are pretty natural by now. While in simple WCF you have to write all the operations (get operations) to just get what you want, in WCF RIA Services, you don't have to do that. You can tell on the client what exactly do you need. (Of course it has constraints). The query by the way runs on the server, you just get the results. I'm pretty sure that's not conflicting with n-tier concepts. (We have presentation tier and a WCF (service layer) as well, and an EF provides DAL like ORM layer:) Definitely not so clear as your solution however your approach takes a lot of extra work to develop. This is out of the box.

  • zoltan.arvai

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by zoltan.arvai on Jun 16, 2010 15:22
    Mr Promo. You dont have to go with the generated code if you don't want to... You can write it from ground up.
  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by Rüdiger on Jul 14, 2010 12:00

    1. I have lots of business functions residing on the server in Plain Old CLR Objects (simple objects and collections). What I am looking for is an example using WCF/RIA for the client-server part. Does anyone have a URL?

    2. We always see the database loading examples, and I am thankful to those providing these;

    However, what I think would be interesting is to see e.g. a SL treeview in MVVM in concert with a SL interactive chart in MVVM which could be bound to an observable collection that automatically updates server data via WCF using a custom (POCO) object structure, and only in the end accessing the data layer with either DataSet or EF or SQL connection.

     

  • -_-

    RE: Building N-Tier business applications with WCF RIA Services – Part II


    posted by zoltan.arvai on Jul 14, 2010 14:37

    Rüdiger: No charts here or RIA Services, just plain old WCF, and POCO  with SL + MVVM (didn't even touch a real persistance layer) I think it show the concepts you need in your above example.

    http://www.silverlightshow.net/items/Data-Driven-Applications-with-MVVM-Part-I.-The-Basics.aspx

  • WaleedSeada

    Re: Building N-Tier business applications with WCF RIA Services – Part II


    posted by WaleedSeada on Jul 30, 2011 22:22

    Hello Zoltan,

    Just a simple question regards the entitycollection: public EntityCollection<Products> Products;

    can we have a client side validation on that collection to check for duplicates in entity entries or something like that using the shared.cs files after decorating the property with the [CustomValidation(typeof(CustomDailyValidation), "ValidateDuplicateIds")] attribute?

    so the final result qould be :

    [CustomValidation(typeof(CustomValidation), "ValidateDuplicateIds")] 
    public EntityCollection<Products> Products;

    If you can give a small example would be great.

    Regards

    Waleed

     

Add Comment

Login to comment:
  *      *       

From this series