WAQS: Write your code faster and better

WAQS is a code-generation solution to speed up and improve quality of.Net data centric applications development. It abstracts away the technical code in order to let developers focus on value add tasks: screens and business code. It’s great for both server and server+client projects.

 

How exactly does WAQS help? Look at the typical 3-tiers architecture diagram below. WAQS lets you write only screens and business code (shaded light blue), and it generates all the boilerplate for you (dark blue).

You can see a more detailed schema here.

 

Of course, if you generate a big part of the code, you are more productive.
But WAQS is more than a tool to improve productivity, WAQS is a new way to code!

WAQS tweet 2

  

7 reasons to use WAQS

 

1. WAQS allows you to write your business code only once in one location

With 3-Tiers application. Where is your business code?
One part is on server, one part is on client. In the server, one part is on services, one part is on entities. In the client, one part is on ViewModels, one part is on Models.

In addition, you can have a lot of business code duplication.

Imagine a Northwind like Database with Orders and OrderDetails.

You want to add a property Amount on OrderDetail with this formula

                Amount = Quantity * UnitPrice * (1 – Discount)

And a property Total on Order with this one:

                Total = OrderDetails.Sum(od => od.Amount)

Imagine that while you are creating an order, you want to show its total.
With a “classic” development, we have to define a property Total in a ViewModel that encapsulates the Order Model class or in the Order class itself.
Then, we have to define the property changed logic that depends on Total formula.

Now imagine that we want to be able to create Invoice in the server and we want to save the Order Total in the Invoice entity to be able to get an old invoice even if Total formula changed.
So for this, we write our formula in the server too.

Then imagine that we want to load all orders that have a Total greater than $1000 using LINQ To Entities.
Entity Framework does not know Total property so we can’t use it in our LINQ query and we have to duplicate the code one more time.

So we have to write it four time. What a pain!

In addition, imagine that we want to change this formula. We have to change many pieces of code and it’s very bad for maintainability.

 

With WAQS, you define your business code only once.

For our example we can define these methods:

public static double GetAmount(this OrderDetail od)
{
   
return od.Quantity * od.UnitPrice * (1 - od.Discount);
}                                           
public static double GetTotal(this Order o)
{
   
return o.OrderDetails.Sum(od => GetAmount(od));
}

public static void Invoice(int orderId, INorthwindService context)
{
   
var order = context.Orders.IncludeOrderDetails().First(o => o.Id == orderId);
   
var invoice = new Invoice 
{
//...
Total = GetTotal(order)
};
    context.Invoices.Add(invoice);
   
foreach (var orderDetail in order.OrderDetails)
        context.InvoiceDetails.Add(
new InvoiceDetail 
{
//...
Amount = GetAmount(orderDetail)
});
    context.SaveChanges();
}


  



This code is named “specifications” in WAQS vocabulary.









Specifications methods are never executed.









In fact this code is just a model that is analyzing by WAQS using Roslyn in order to generate other pieces of code in the client and in the server.









In addition, WAQS uses its own LINQ provider based on Entity Framework one and that supports generated property like Total. So it means that instead of this “classic” LINQ To Entities query:









context.Orders.Where(o => o.OrderDetails.Sum(od => od.Quantity * od.UnitPrice * (1 – od.Discount)) > 1000)









Can be written like this using WAQS:



context.Orders.Where(o => o.Total > 1000)




 







2. WAQS allows you to write your code easier with no consideration about technical aspect on business code



Imagine that you want to be sure that every orders have some details.









It seems very easy but it is not because of concurrency aspect when you want to delete an OrderDetail for example.









In this case, you can override Entity Framework context SaveChanges method for example.















public override int SaveChanges()
{
   
if (((IObjectContextAdapter)this).ObjectContext.ObjectStateManager
       
.GetObjectStateEntries(EntityState
.Added)
        .Select(ose => ose.Entity).OfType<
Order>().Any(o => ! o.OrderDetails.Any()))
       
throw new InvalidOperationException("Orders must have details");

   
var orderDetailsIds = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager
       
.GetObjectStateEntries(EntityState.Modified | EntityState
.Deleted)
        .Where(ose => ose.State ==
EntityState.Deleted || ose.GetModifiedProperties().Contains("OrderId"))
        .Select(ose => ose.Entity).OfType<
OrderDetail>().Select(od => od.Id);
   
using (var transaction = new TransactionScope())
    {
       
var orderIds = OrderDetails.Where(od => orderDetailsIds.Contains(od.Id))
            .Select(od => od.OrderId).Distinct().ToList();
       
var value = base.SaveChanges();
       
if (Orders.Where(o => orderIds.Contains(o.Id) && !o.OrderDetails.Any()).Any())
           
throw new InvalidOperationException("Orders must have details");
        transaction.Complete();

        return value;
    }
}





 









This code is complex and if we hide exception message, it is not easy to understand developer intention just reading it.









In addition, many developers forget concurrency and write a code with bugs that are hard to reproduce.











 







As I wrote previously, WAQS analyzes specifications and generates code based on them.









With WAQS, you can write your validation rule like this:









public static Error ValidateOrderHasOrderDetails(this Order o)
{
    if
(! o.OrderDetails.Any())
        return new Error { Criticity = Criticity.Error, Message = "Orders must have details"
};
    return null
;
}




 







Based on it, WAQS generates a code that takes care about concurrency to validate the fact that orders must have details.









So the code you have to write is easier. It is also easier to read and so easier to maintain.













 







3. WAQS can be used as a super data access layer on top of Entity Framework



As we saw previously, WAQS uses its own LINQ provider which supports specifications generated properties.









Furthermore, WAQS LINQ provider has a better support of LINQ than LINQ To Entities (for DateTime calculation for example) and also has an easy way to load calculated properties with entities.









For example, if you want to load orders with their total without loading all order details you can use this query with LINQ To Entities:



context.Orders
    .Select(o =>  
        new 
        { 
             Order = o, 
             Total = (double?)o.OrderDetails.Sum(od => od.Quantity * od.UnitPrice * (1 – od.Discount)) 
        })
    .AsEnumerable()
    .Select(o =>  
        { 
            o.Order.Total = o.Total ?? 0; 
            return o.Order; 
        })



  



Now with LINQ To Entities + WAQS (L2EW) we can use this query:



context.Orders.WithTotal()


    



4. WAQS can be used as a sort of remote Entity Framework on steroids



For data centric application, it could be very useful to be able to querying directly from the client.









Microsoft built WCF Data Services for this usage.









However WCF Data Services has a lot of restriction on querying.
Indeed, only very basic queries are supported. In the other hand, LINQ To WAQS (the client LINQ provider of WAQS) supports everything supported by L2EW and so more than LINQ To Entities in the client!









Imagine that you want to load orders with their total when this one is greater than $1000 from the client.









With WCF Data Services, you have to define this method on the service:









[WebGet]
public IEnumerable<OrderWithTotal> GetOrdersGreaterThan(double min)

    using (var context = new NorthwindEntities()) 
    { 
        foreach (var o in context.Orders
            .Select(o =>  
                new 
                { 
                    Order = o, 
                    Total = (double?)o.OrderDetails.Sum(od => od.Quantity * od.UnitPrice * (1 – od.Discount)) 
                }).Where(o => o.Total > min)
            .AsEnumerable()
            .Select(o => 
                new OrderWithTotal 
                { 
                    Id = o.Order.Id, 
                    /* Because adding a new property on entities classes in not enough, we defined an entity OrderWithTotal with same properties than Order + Total property */ 
                    Total = o.Total ?? 0
                }))

 
            yield return o; 
    }
}



  



Then, in the client, you have to use call the GetOrdersGreaterThan:



private async Task LoadOrdersAsync()
{
    var uri = ConfigurationManager.AppSettings["NorthwindServiceUri"];
    var context = new NorthwindEntities(new Uri(uri));
    Orders = (await Task<IEnumerable<OrderWithTotal>>.Factory
        .FromAsync(
context.BeginExecute<
OrderWithTotal>(new Uri(string.Concat(uri, "/GetOrdersGreaterThan?min=1000")), null, null),
context.EndExecute<
OrderWithTotal>))
.Select(o =>
new Order 
{
Id = o.Id, 
/* Copy all Order properties */ 
Total = o.Total });
}


  



With WAQS, we can just use this query:



private async Task LoadOrdersAsync()
{     Orders = await _context.Orders.AsAsyncQueryable().Where(o => o.Total > 1000).WithTotal().ExecuteAync(); }


  



In addition, contrary to WAQS (or Entity Framework), WCF Data Services does not have a transparent tracking tracking client context.









So if you want to add a new OrderDetail to an Order, you have to use this code with WCF Data Services:









o.OrderDetails.Add(od);
od.OrderId = o.Id;
od.Order = o;
context.AddToOrderDetails(od);
context.AddLink(o, "OrderDetails", od);


  



With WAQS, this code is enough:



o.OrderDetails.Add(od);


  



5. WAQS is super-fast!



WAQS improves a lot eager loading. In this sample, EF Include runs in 66 seconds when WAQS one runs in 2.5 seconds!









If we compare it with WCF Data Services we have the following results (time in ms):



 









 

WCF Data Services

WAQS

Get 18 869 customers

6693

1825

Get 18 869 customers name

2652

202

One include for 100 customers

9672

3089

Two includes for 100 customers

27021

3588

Four includes for 100 customers

42604

4056

Four includes for 200 customers

Exception

5045





 











  



6. WAQS helps you with MVVM



In addition to PropertyChanged logic generation based on specifications methods, WAQS can also help for client validation and meta-data.









WAQS also helps to simplify paginated loading.









In addition, for WPF only, WAQS allows you to add some PropertyDescriptor on some entities which could be very useful. For more information read this post.











 







7. WAQS is customizable and open source



WAQS generates a huge part of your code. But this code is completely customizable.









First, WAQS already uses a lot of partial class and partial methods to let you customize WAQS processes.









Then, you can change ttinclude files that are defined for all your projects.









In addition, WAQS is open source, so it is not a black box.











 







Conclusion



WAQS is super useful for 3-Tiers data-centric application with many kinds of C# client.
Some of WAQS beta-testers use it with WPF or Windows / Windows Phone Apps and also iOS and Android using Xamarin.











 



But WAQS can also be used on the server as a super data access layer on top of Entity Framework.
Some of WAQS beta-testers use it with ASP.NET MVC or Console applications.











 



In addition of data access, WAQS proposes a new way to define business code using Roslyn. With this new way, writing business code is faster, easier to write, easier to read, easier to maintain so definitively better!



 



For more information about WAQS, read the documentation.

This entry was posted in 16402, 16868, 17697, 7674. Bookmark the permalink.

2 Responses to WAQS: Write your code faster and better

  1. Matthieu MEZIL says:

    Thanks :)

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>