« Using F# Agent For Concurrent Updates on Entities | Main | RIA Domain Context and Reactive Extensions »
Thursday
Jul142011

Linq Query Expression Syntax with RIA Domain Context

After discussing my previous post with Philip Freeman, he suggested another good example would be to show how to use Linq Query Expression Syntax with RIA Domain Context calls. To help facilitate this example I created two new Extension Methods; LoadEntityWithRx and LoadEntitiesWithRx .

Load Entity With Rx

   1: public static IObservable<T> LoadEntityWithRx<T>(this DomainContext context, EntityQuery<T> query) where T : Entity
   2: {
   3:     return Observable.Create<T>(ob =>
   4:     {
   5:         context.Load(query, ob.ReturnEntity, null);
   6:         return () =>
   7:         {
   8:           // On unsubscription, do nothing
   9:         };
  10:     });
  11: }

Here we are only interested in a single Entity returned by the Load operation of the domain context. This Extension Method follows the same pattern as the ones on the previous blog post. The Load operation is called (line 5) on the Domain Context, and the work is done by the ReturnEntity method. Because nothing needs to be done on unsubscription, an empty Action is returned.

Return Entity

   1: private static void ReturnEntity<T>(this IObserver<T> ob, LoadOperation<T> loadOp) where T : Entity
   2:      {
   3:          if (loadOp.HasError)
   4:          {
   5:              loadOp.MarkErrorAsHandled();
   6:              ob.OnError(loadOp.Error);
   7:          }
   8:          else
   9:          {
  10:              if (loadOp.Entities.Count() != 1)
  11:              {
  12:                  ob.OnError(new Exception("More then one entity was returned"));
  13:              }
  14:              else
  15:              {
  16:                  ob.OnNext(loadOp.Entities.SingleOrDefault());
  17:              }
  18:              ob.OnCompleted();
  19:          }
  20:      }

This is where the work is done; we either pass the exception along to the Observer or pass the Entity along. More work should be done to handle missing Entities (line 10); here I assume there will be exactly one Entity returned, which is not always the case.

Another good side effect of using the Extension Methods is you can consolidate your Error Handling for Domain Service calls.

Load Entities With Rx

   1: public static IObservable<IEnumerable<T>> LoadEntitiesWithRx<T>(this DomainContext context, EntityQuery<T> query) where T : Entity
   2: {
   3:     return Observable.Create<IEnumerable<T>>(ob =>
   4:     {
   5:         context.Load(query, ob.ReturnEntities , null);      
   6:         return () =>
   7:         {
   8:             // On unsubscription, do nothing
   9:         };
  10:     });
  11: }

The same pattern is followed with the LoadEntityWithRx Extension Method. The work is being done with the ReturnEntities method.

Return Entities

   1: private static void ReturnEntities<T>(this IObserver<IEnumerable<T>> ob, LoadOperation<T> loadOp) where T : Entity
   2: {
   3:     if (loadOp.HasError)
   4:     {
   5:         loadOp.MarkErrorAsHandled();
   6:         ob.OnError(loadOp.Error);
   7:     }
   8:     else
   9:     {                
  10:         ob.OnNext(loadOp.Entities);
  11:         ob.OnCompleted();
  12:     }
  13: }

A similar pattern is followed here as well, where Exceptions are passed to the Observer, and the OnNext method is called to pass along the Entities.

Query Expression Syntax Example

 

In this example I have created are really bad model for Customer and Orders.

   1: public class Customer
   2: {
   3:     [Key]
   4:     public Guid Id { get; set; }
   5:  
   6:     public string Name { get; set; }
   7: }
   8:  
   9: public class Order
  10: {
  11:     [Key]
  12:     public Guid Id { get; set; }
  13:  
  14:     public Guid CustomerId { get; set; }
  15:  
  16:     public string Desc { get; set; }
  17: }

Good Example:

If you want to Load an Entity i.e. a Customer and then you want to take data from that Entity i.e. Customer Id, and Load another Entity the nested callbacks can get really ugly (example below). I will show how it works with Linq Query Expression Syntax, and then I will show how it would be done normally.

   1: private void LoadCustomer()
   2: {
   3:     CustomerDomainContext context = new CustomerDomainContext();
   4:  
   5:     EntityQuery<Customer> customerQuery = context.GetCustomersQuery().Where(c=>c.Name == "Fred");
   6:  
   7:     var customerOrders = from customer in context.LoadEntityWithRx(customerQuery)
   8:                          let orderQuery = context.GetOrdersQuery().Where(o => o.CustomerId == customer.Id)
   9:                          from orders in context.LoadEntitiesWithRx(orderQuery)
  10:                          select new { Customer = customer, Orders = orders };
  11:  
  12:     customerOrders.Subscribe((custOrders) => 
  13:     { 
  14:         // do cool stuff
  15:     });                                 
  16: }

In the example above you can reason about what the code is doing. First query on user name of “Fred” (Lind 5) then get the Customers Id (Line 8), then Query on the Orders matching that Customer Id (Line 9). Finally it is returning to the subscriber as an Anonymous Type containing the Customer object and its Orders (Line 10).

Bad Example:

   1: private void LoadCustomersBad()
   2: {
   3:     CustomerDomainContext context = new CustomerDomainContext();
   4:     EntityQuery<Customer> customerQuery = context.GetCustomersQuery().Where(c => c.Name == "Fred");
   5:  
   6:     context.Load(customerQuery, (loadOp) => 
   7:     {
   8:         if (!loadOp.HasError)
   9:         {
  10:             if (loadOp.Entities.Count() <= 1)
  11:             {
  12:                 Customer customer = loadOp.Entities.Single();
  13:                 EntityQuery<Order> orderQuery = context.GetOrdersQuery().Where(o => o.CustomerId == customer.Id);
  14:                 context.Load(orderQuery, (loadOpOrder) => 
  15:                 {
  16:                     if (!loadOpOrder.HasError)
  17:                     {
  18:                         IEnumerable<Order> orders = loadOpOrder.Entities;                                
  19:                         // do cool stuff
  20:                     }
  21:                 }, null);
  22:             }
  23:         }
  24:     }, null);
  25: }

Here the same result is accomplished, but it is so much harder to reason about the code. Imagine if you had to maintain this code. I have seen a lot of production code where the developer used 5+ nested callbacks; not fun. Maintaining the first code sample will be so much easier.

The same code is being called, but with some well-placed Extension Methods we can use the power and ease of Linq Query Expression Syntax to make code more maintainable.

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (12)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Response: Scott Tucker
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Response: descargar fraps
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    When used in windows workflow foundation contexts, XAML can be used to describe possibly long-running declarative logic, for instance those created by process modeling tools and principles techniques.
  • Response
    Response: sexercise
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context
  • Response
    Response: Xovilichter
    Tony Abell Blog - Blog - Linq Query Expression Syntax with RIA Domain Context

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>