December 2013 - Posts

How to define business logic with WAQS? 13/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

How to define business logic with WAQS? 8/13

How to define business logic with WAQS? 9/13

How to define business logic with WAQS? 10/13

How to define business logic with WAQS? 11/13

How to define business logic with WAQS? 12/13

 

Server filters

We saw that, using WAQS, we can querying directly from the client.

But, when we do this, we sometimes want to be able to filter the result in the server in order to not expose everything to everybody. So it’s important to be able to add some filters in the server on the data sent to the client.

One of the good things with code generation is the ability to use partial methods which often is a very good, flexible and easy way to define configuration IMHO and WAQS uses it a lot.

For example, if you only want to return the orders of the connected customer, you just have to extend NorthwindEntitiesFilters class:

partial class NorthwindEntitiesFilters
{
    partial void ApplyOrdersServerWhere(ref Expression<Func<Order, bool>> filterExpression)
    {
        string customerId = GetCurrentCustomerId();
        filterExpression = o => o.CustomerId == customerId;
    } }

Of course, this filter will be included in LINQ To Entities query anytime we use the Order class in LINQ To WAQS query and therefore will be included in the SQL query.

 

Note that, for a better understanding of the next posts, we won’t keep this filter.

How to define business logic with WAQS? 12/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

How to define business logic with WAQS? 8/13

How to define business logic with WAQS? 9/13

How to define business logic with WAQS? 10/13

How to define business logic with WAQS? 11/13

 

Specification methods and DTO

Why using a DTO instead of an entity?

Using a DTO could be very complex if this one is modified on the client side and if you want to save your changes.

So in this case, I definitively prefer directly use my entities and, in case, having many models with, sometimes, just some pieces of the entire entity. (I will write about it in a future post)

However, for read only classes, it could be very useful:

  • to send some aggregates (even if we can do it using WAQS specifications)
  • to use some classes that are not persisted

We previously saw that we can use DTO in LINQ To WAQS queries.

In some cases, we want to expose DTO or collection of DTO through WCF methods.

For this, we could use the fact that the WCF Service is partial but we won’t talk about it here.

In our case, we will use Service methods which return DTO.

These ones would be able to use some Calculated properties on DTO but the business logic won’t be use in the client in this case (the calculation will be made on the server and only the value will be send to the client).

How to define business logic with WAQS? 11/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

How to define business logic with WAQS? 8/13

How to define business logic with WAQS? 9/13

How to define business logic with WAQS? 10/13

 

Service methods

A service method in WAQS vocabulary is neither a calculated property, nor a metadata definition nor a validation method.

Service methods could be used to define some process.

So for example, you could define a method MakeInvoice that makes an invoice from an Order.

public static Invoice MakeInvoice(this Order o)
{
    var invoice = new Invoice
{
OrderId = o.Id,
CustomerId = o.CustomerId,
Total = GetTotal(o),
Dicount = GetDiscount(o)
};
    if (o.Customer != null)
    {
        invoice.CustomerCompanyName = o.Customer.CompanyName;
        invoice.CustomerContactName = o.Customer.ContactName;
    }
    foreach (var od in o.OrderDetails)
        invoice.InvoiceDetails.Add(
     new InvoiceDetail 
{
OrderDetailId = od.Id,
Quantity = od.Quantity,
UnitPrice = od.UnitPrice,
Discount = od.Discount,
Amount = GetAmount(od)
});
    return invoice;
}

From this code, WAQS generates a MakeInvoice method in Order class on the client and a method in the client context with the Order as parameter that calls the first one.

A service method is not necessary an extension method but, anyway, it’s a static method. If it’s only a static method, WAQS just generates a method in the client context.

Note that for polymorphism, it must be an extension method.

 

Great! But we often want to define this kind of method in the server (SOA).

With WAQS, if you use the DAL context or the service (I will write about it at the end of this post) or if you use NotApplicableOnClient attribute, WAQS only generates a method awaitable in the client context. For example, imagine that you want to persist the new Invoice in your MakeInvoice method. You can do it like this:

public static Invoice MakeInvoice(this Order o, INorthwindWAQSEntities context)
{
    var invoice = new Invoice
        {
           OrderId = o.Id,
           CustomerId = o.CustomerId,
           Total = GetTotal(o),
           Dicount = GetDiscount(o)
        };
    if (o.Customer != null)
    {
        invoice.CustomerCompanyName = o.Customer.CompanyName;
        invoice.CustomerContactName = o.Customer.ContactName;
    }
    foreach (var od in o.OrderDetails)
        invoice.InvoiceDetails.Add(
     new InvoiceDetail 
        {
            OrderDetailId = od.Id, 
            Quantity = od.Quantity,
            UnitPrice = od.UnitPrice,
            Discount = od.Discount,
            Amount = GetAmount(od)
        });
    context.Invoices.Add(invoice);
    context.SaveChanges();
    return invoice;
}

Now, on the client, we just have an awaitable method in the client context that we can call like this:

await _context.MakeInvoiceAsync(o);

 

In my sample, I used the DAL context (INorthwindWAQSEntities) but I could also use the service (INorthwindService).

If you are sure about what you are doing you can use the DAL context. Using the service instead includes all validation rules in the saving process.

 

Note that because of WCF usage, service methods name must be unique.

How to define business logic with WAQS? 10/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

How to define business logic with WAQS? 8/13

How to define business logic with WAQS? 9/13

 

Validation rules with navigation properties

Imagine that we want to be sure that an order does not exist without any detail.

This basic feature is not as easy as it seems because of concurrency.

Indeed, if it’s easy for insertion, it’s more complex if you want to remove some OrderDetails or if you want to change their OrderId.

In this case, the process is the following:

  • open a transaction
  • query the DB to get get deleted/with OrderId changed OrderDetails’ OrderId with a DB lock
  • save changes
  • check if orders with ids we got at step 2 still have some details
  • commit or rollback the transaction.

So this code is not so easy and many junior developers could forget concurrency aspect.

In addition, even if they write it with a good way, there is another issue: the developer intention is lost reading this code because of technical code. And this makes maintainability more complex.

With WAQS, you can write this validation rule with another way:

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

Based on this super easy code with no technical aspect and because we use a navigation property, WAQS generates the complex code we just explained.

 

Note that in the current version, this just works with navigation property many. But implementing it with navigation property one is in my backlog.

How to define business logic with WAQS? 9/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

How to define business logic with WAQS? 8/13

 

Transactional validation business rules

If you really want to be sure that the couple (CompanyName, ContactName) on customers is unique and if you don’t want to express it in the DB to not spit your business logic between the DB and the code for example, you have to use a transaction for concurrency aspects.

To express it with WAQS, you can use a Validate method that not returns an error but that return a Func<Error>:

public static Func<Error> ValidateCustomerNameUnique(this Customer c, INorthwindWAQSEntities context)
{
     return () =>
     {
         if (c.CompanyName != null && c.ContactName != null &&
context.Customers.Any(c2 => c2.Id != c.Id &&
c2.CompanyName == c.CompanyName && c2.ContactName == c.ContactName))
             return new Error
{
Criticity = Criticity.Error,
Message = "A customer with the same company name and the same contact name already exist"
};
         return null;
     }; }

Using it, WAQS will generate a SaveChanges method that opens a transaction, saves changes, checks that every added / modified customers have an unique couple (CompanyName, ContactName) and finally commit (if check passed) or rollback (if check failed) the transaction.

Note that in this case, you can’t use ExposeAsService attribute anymore. It is just usable and used on server SaveChanges method.

However, it can be combined with ValidateOnServer attribute.

How to define business logic with WAQS? 8/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

How to define business logic with WAQS? 7/13

 

Validate methods and CUD

In our previous sample, the validation rule will be apply on SaveChanges when the entity is Added or Modified.

But perhaps we also want to apply it on Delete or perhaps we just want to apply it on Add.

It’s possible with WAQS to change the default case (Modified and Added) using two new attributes: ValidateOnServer and ValidateOnClient.

[ValidateOnServer(Mode = ValidateMode.OnInsert | ValidateMode.OnUpdate)]
[ValidateOnClient(Mode = ValidateMode.OnInsert)]

As we showed in previous posts, you have to transform T4 templates after changing your specifications.

How to define business logic with WAQS? 7/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

How to define business logic with WAQS? 6/13

 

Exposing Validate method on the service

With the same way than for calculated properties, you can use NotApplicableOnClient attribute to specify that a validation method is only applicable on the server on saving and still with the same way, if you use the DAL context on method parameters, it will only be usable from the server too.

However, in some cases we want to check if our data are correct from the client anyway.

For example, imagine that we don’t want to be able to create a new Customer if another one already exists with the same CompanyName and ContactName.

For this, we will use the DAL context on our Validate method.

In the UI, perhaps we want to check it when the user mentioned these information before setting all the other ones.

In order to be able to call it from the client, we will expose the method in the WCF Service.

To do it, we will use an attribute ExposeAsService in our specifications:

[ExposeAsService]
public static Error ValidateCustomerNameUnique(this Customer c, INorthwindWAQSEntities context)
{
     if (c.CompanyName != null && c.ContactName != null &&
          context.Customers.Any(c2 =>
              (c.Id == null ||  c2.Id != c.Id) &&
              c2.CompanyName == c.CompanyName &&
              c2.ContactName == c.ContactName))
         return new Error
         {
             Criticity = Criticity.Error,
             Message = "A customer with the same company name and the same contact name already exist"
         };
     return null; }

After transforming T4 templates we will find a method ValidateCustomerNameUnique on our service and the client context allows you to use it very easily:

var error = await _context.ValidateCustomerNameUniqueAsync(c);  

 

Note that if you use Transform All T4 templates it’s possible that generated code does not compile. Indeed, the client context will encapsulate the WCF proxy ValidateCustomerNameUnique method. But, because the server compilation does not append yet, the WCF proxy does not include this method. In this case, after compiling the server, you can just transform the proxy template.

How to define business logic with WAQS? 6/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

How to define business logic with WAQS? 5/13

 

Non metadata validation rules

Metadata allows to define some basic validation rules. But sometimes, validation is more complex. For example imagine that we want to be sure that order’s OrderDate is always less than ShippedDate with an error if OrderDate is greater and a warning if dates are equal.

With WAQS, you can do it like this:

public static Error ValidateShipDateNotLessThanOrderDate(this Order o)
{
    if (o.ShippedDate.HasValue && o.ShippedDate.Value < o.OrderDate)
        return new Error { Criticity = Criticity.Error, Message = "Shipped date can't be less than order date" };
    return null;
}

public static Error ValidateShipDateGreaterThanOrderDate(this Order o)
{
    if (o.ShippedDate.HasValue && o.ShippedDate.Value == o.OrderDate)
        return new Error { Criticity = Criticity.Warning, Message = "Shipped date should be greater than order date" };
    return null;
}

// Of course, it’s probably better to use a resource file (I will show you how in a future post) but for this post I prefer having a very basic sample.

Like with calculated properties, validation methods use convention: extension method on entity and starts with “Validate”.

After transforming T4 templates, this validation will be effective on the server and the client if one of these two properties (ShippedDate and OrderDate) is modified.

In the client, we can use the same way than with metadata. So in a WPF app, we can use the following XAML:

<StackPanel>
    <DatePicker SelectedDate="{Binding Order.OrderDate}"
               controls:ErrorsBehaviors.Errors="{Binding Order.OrderDateErrors}" />
    <DatePicker SelectedDate="{Binding Order.ShippedDate}"
               controls:ErrorsBehaviors.Errors="{Binding Order.ShippedDateErrors}" />
</StackPanel>

How to define business logic with WAQS? 5/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

How to define business logic with WAQS? 4/13

 

Metadata definition

WAQS uses edmx metadata for validation: MaxLength (for string and byte array) and Nullable.

However, edmx metadata often are insufficient: no MinValue, no MaxValue, no MinLength, no Pattern and even less dynamic metadata.

In specifications, you can fix it using some static methods to define metadata.

These methods must start by “Define”, without any parameter and returning void.

public static void DefineOrderDetailMetadata()
{
    Metadata<OrderDetail>.DefineMinValue(od => od.Discount, 0);
    Metadata<OrderDetail>.DefineMaxValue(od => od.Discount, 1);
}

With this way you can define static metadata but also dynamic one:

public static void DefineCustomerMetadata()
{
    Metadata<Customer>.DefinePattern(c => c.PostalCode, c => c.Country != null && 
      c.Country.ToUpper() == "FRANCE" ? @"^(\d{2}|(2(A|B)))\d{3}$" : null);
    Metadata<Customer>.IsNullable(c => c.City, c => c.PostalCode == null);
}

WAQS will generate some validation code using these metadata and edmx ones.

The SaveChanges method (generated  by WAQS) verifies this business rules before Saving changes into the DB (I will explain in a future post how it works).

In the client side, there is a metadata validation too and there are two ways to use them with WPF and one with PCL.

WPF

The first way is to use WPF DataErrorInfo. WAQS generates the DataErrorInfo logic. So, in your binding, you can just set ValidatesOnDataErrors to true

<TextBox Text="{Binding OrderDetail.Discount, ValidatesOnDataErrors=true}" />

Then, if you write something wrong the control border will be red (by default).

However, I don’t find neither DataErrorInfo nor NotifyDataErrorInfo (for WPF 4.5 only and not used by WAQS) enough good. Indeed, we can’t easily have many errors for example or mixing some errors and some warnings.

For this, WAQS proposes another way. It proposes to use a collection of errors for each scalar properties of an entity.

WAQS for WPF proposes this property as a new PropertyDescriptor on entity type (which is better for developer experience IMO) and, in addition, it generates a behavior to show errors.

So, in our sample, you can use the following XAML code:

<TextBox Text="{Binding OrderDetail.Discount}"
         controls:ErrorsBehaviors.Errors="{Binding OrderDetail.DiscountErrors}" />

With this way, the border has the color of this higher error (red if error, orange if warning) with a tooltip which list the different errors:

image

Note that, as very often with WAQS, you have a partial method in ErrorsBehaviors class to change the style.

static partial void SetControlStyle(Control control, ObservableCollection<Error> errors, Criticity maxCriticity, 
ref bool done);

In addition, you can also use other Metadata like MaxLength to bind them (and so avoid possible errors):

<TextBox Text="{Binding Customer.CompanyName}" MaxLength="{Binding Customer.CompanyNameMaxLength}" />

PCL

With PCL, we can’t use behavior of PropertyDescriptors. So WAQS just generate some properties (DiscountErrors in the OrderDetail and CompanyNameMaxLength in Customer in our sample).

How to define business logic with WAQS? 4/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

How to define business logic with WAQS? 3/13

 

Server calculated properties

Sometimes, we want to calculate the property on the server only.

For this, WAQS has an attribute to specify it: NotApplicableOnClient.

Sometimes, we need to execute a query into the DB to get the value of the calculated property.

For this, you can just use the DAL Context Interface as a parameter of the method. If the method if an extension method on an entity and only has the entity and the context as parameter, it will be transformed into a property by WAQS.

public static int GetOrdersCount(this Customer c, INorthwindWAQSEntities context)
{
    return context.Orders.Where(o => o.CustomerId == c.Id).Count();
}

If you use the context, the attribute NotApplicableOnClient is useless. The calculation will only be on the server anyway.

In this case, we would be able to use the property on the server. On the client, we will only have this code that I will explain in a future post:

public int OrdersCount
{
    get
    {
        return Specifications != null && Specifications.HasOrdersCount ? Specifications.OrdersCount : default (int);
    }
    set
    {
        throw new System.InvalidOperationException();
    }
}

How to define business logic with WAQS? 3/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

How to define business logic with WAQS? 2/13

 

Calculated properties and polymorphism

Sometimes we need to use polymorphism with calculated properties.

In our sample, we could want to apply a 10% discount to VIPCustomers.

How to express it? You could not use asbtract / virtual / override keywords in static methods.

Because WAQS will only use this code to generate another one, we can just use attributes on the specifications.

So our sample could be written like this:

[Virtual]
public static double GetDiscount(this Customer c)
{
    return 0;
}

[Override]
public static double GetDiscount(this VIPCustomer c)
{
    return 0.1;
}

WAQS code analysis will see these attributes and will generate adequate code.

So in Customer class, WAQS will generate this code:

public virtual double Discount
{
    get
    {
if (Specifications != null && Specifications.HasDiscount)
            return Specifications.Discount;
        return 0;
    }
    set
    {
        throw new System.InvalidOperationException();
    }
}

And this one in VIPCustomer class:

public override double Discount
{
    get
    {
        if (Specifications != null && Specifications.HasDiscount)
            return Specifications.Discount;
        return 0.1;
    }
    set
    {
        throw new System.InvalidOperationException();
    }
}

So with this way you have the expected polymorphism

How to define business logic with WAQS? 2/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

How to define business logic with WAQS? 1/13

 

Entity calculated properties

WAQS server generation created a Specifications Folder. It’s where we will define (by default) our business code.

As I previously wrote, the idea is to write our business code outside the rest of the code. So it won’t be possible to extend entities class using partial keyword.

In our case, we will use static methods.

WAQS is based on convention. This way homogenizes the business code potentially written by many developers.

Calculated properties must start with “Get” and been an extension method on an entity (with only this entity as method parameter).

For example, if we want to define a CustomerName property for Customer type which is the aggregation of CompanyName and ContactName, we can write this:

public static class NorthwindSpecifications
{
    public static string GetCustomerName(this Customer c)
    {
        return string.Concat(c.CompanyName, " - ", c.ContactName);
    }
}

After transforming T4 templates, we now have a property CustomerName generated on the client Customer class and its get has our code:

public string CustomerName
{
    get
    {
if (Specifications != null && Specifications.HasCustomerName)
         return Specifications.CustomerName;
        return string.Concat(this.CompanyName, " - ", this.ContactName);
    }
   
set
    {
        throw new System.InvalidOperationException();
    }

}

I will explain later the if and the set.

 

Now imagine that you add this new calculated property

    public static string GetCustomerName(this Order o)
    {
        return o.Customer.GetCustomerName();
    }

In this case, the generated property is the following:

public string CustomerName
{
    get
    {
        if (Specifications != null && Specifications.HasCustomerName)
            return Specifications.CustomerName;
        if (this.Customer == null)
            return default (string);
        return this.Customer.CustomerName;
    }
    set
    {
        throw new System.InvalidOperationException();
    }
}

So, as you can see, WAQS adds test to avoid NullReferenceException.

How to define business logic with WAQS? 1/13

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

How does WAQS querying work?

 

Where do we write business code?

Maintainability issue

The business code often is spread out pretty much everywhere in the application: in entities on the server side, in the entities on the client side, in services, in ViewModels.

This is described by the “spaghetti code” expression. It makes maintainability harder.

Furthermore, we often have a business code duplication between the server and the client sides which is not good and which is a potential risk when you have to change your business logic.

In addition, business code often is mixed with technical one. It also makes maintainability harder because the developer intention could be lost in a complex code in this case.

WAQS business code approach

WAQS address this point with a way very different than usual. With WAQS, business code is written outside the rest of the code without any technical aspect.

This code will never be executed. It will only be used by WAQS T4 templates as a model. Indeed T4 templates analyze this code using Roslyn and will inject it in the different layers mixing it with technical code on T4 generation process.

With this way, code writing and maintainability become easier.

Furthermore, contrary to private rules engines, WAQS doesn’t use any “black-box”. The T4 meta-code is available and the result code is generated in your project so it will be very easy to debug which can help you to understand it.

In addition, contrary to an XML description with a limited expressiveness, WAQS lets you use C# to write our business code.

Some solutions (often based on DSL) propose a designer above the XML. However, it is not as good as C# for productivity and, in addition, developers often dislike this way on which they don’t code anymore and they “just draw some rectangles and arrows”. With WAQS, they continue doing what they know and like: coding!

WAQS identifies three business rules kinds:

  • Calculated properties
  • Validation methods
  • Service methods

By default, you have to define your business code in the Specifications folder created by WAQS NuGet command execution.

How does WAQS querying work?

Previous posts on WAQS:

WAQS: Introduction

How to use WAQS?

Inflexible services issue

The issue when you use a classic WCF service and when you have many queries is the fact that it’s often a pain due to the flexibility missing.

For example, with Northwind,

If you want to get all customers, we will have a GetCustomers method on the service.

If you want to get the customers living in Paris, we will add a GetCustomersByCity method.

If you want to get the customers, per their age, we will add a GetCustomersByAge method.

If you want to get the customers, living in Paris per their age and using pagination, we will again add a new method.

You can try using one method with many arguments but it implies many many many parameters or many limitations.

So it will quickly become a nightmare!

Moreover, coding a new application or some new features will probably require to modify the service which prevent some scenarios as having a client coded by a third party.

Ideally, for Data Centric applications, we want to be able to querying directly from the client.

To do it, Microsoft proposes a technology: WCF Data Services based on OData protocol.

If OData is a very interesting solution, it has a big issue that could be crippling in many projects: querying limitation.

Indeed, with OData you can only use basics WHERE, ORDER BY, SELECT and pagination.

For example, it is not possible to get the total spent by each customer using OData.

In order to querying, WAQS proposes a really more powerful LINQ provider with a feature perimeter even better than LINQ To Entities one.

In this post (and many future ones), we will work with the following model.

image

To get the total spent by customer, you can use the following query with WAQS:

var totalSpentPerCustomer = await (from c in _context.Customers.AsAsyncQueryable() 
                                                          let totalSpent = (double?)c.Orders.Sum(o => 
                                                              o.OrderDetails.Sum(od => od.UnitPrice * od.Quantity * (1 - od.Discount)))
                                                          select new CustomerWithTotalSpent { Customer = c, TotalSpent = totalSpent ?? 0 })
                                                         .ExecuteAsync();

 

How does WAQS work?

First, contrary to classic IQueryable approach, WAQS considers that the fact to call the server (and then the database) should not be transparent.

With WAQS, if you use a LINQ query on _context.Customers, you are executing a LINQ To Object query on customers already loaded in the context cache. To call the server, you have to use the AsAsyncQueryable method.

Moreover, it’s important to use some asynchronous server queries in order to not freeze the UI.

IQueryable interface, classically used for querying does not allow asynchronous execution. WAQS generates two new interfaces: IAsyncQueryable<T> (to get a collection) and IAsyncQueryableValue<T> (to get only one instance, after a First or a Count for example).

AsAsyncQueryable method returns an instance of IAsyncQueryable<T>.

Every LINQ extension methods are generated by WAQS for IAsyncQueryable<T>.

Query serialization

In order to be able to execute the query on the server side, we have to send it the query. It’s done by Execute method.

The issue in this case is the fact that LINQ expressions are not serializable.

So, WAQS defines its own SerializableExpression in order to serialize Expression information to be able to build the LINQ Expression on the server side and then been able to execute this query using Entity Framework.

Then, we have a new issue. To build the LINQ expression on the server type, we have to know every query used types. But the anonymous type used in the previous query is not known by the server.

To fix this issue, the SerializableExpression includes used types description when these types are not defined in mscorlib.dll with a namespace starting with System and when the type does not have the DataContract attribute.

This description includes the name and the type of each of its read/write properties.

On the server side, when the type is unknown, WAQS uses the type description to generate the type at runtime (using IL emit).

Then this type is put in a cache in the server in order to not have to create it anytime.

Now that this issue is fixed, there is a new one: the result serialization.

The queries result type is a NorthwindQueryResult (in our sample).

image

If the type is known, WAQS uses Value or Values properties function of the return type (one instance or a collection).

If the type is not known, WAQS serializes every property one per one applying the same logic.

To optimize performances, serialization procedure makes an ExpressionTree, at runtime, which will be compiled in order to have a delegate that can be pushed into a cache in the server.

Like this, next time we will execute a query returning the same type, the serialization process will use the delegate, instead of using serialization, like if we wrote the code on the server to serialize the type.

On the client side, if the type is known on the server, WAQS uses Value or Values properties to return the query result. Else, using Reflection, WAQS builds the result using QueryResultRecords.

Note that, on the client side, we could build an expression in order to put into the cache the instantiation process (as it does on the server). However, it is less important than in the server. And, in addition, there is a better way using DTO.

 

DTO Usage

Using a server unknown type as query result implies a more important instantiation cost.

Indeed, even with optimizations previously described, the serialization property by property could be significant.

So, to optimize the process, the best is to define the type on the server.

As wrote in introduction, one of the WAQS idea is to pick out the technical code. So this class don’t need DataContract / DataMember attributes.

By default, after NuGet code generation, WAQS creates a DTO folder in the server. In this folder, we will define the following class:

public class CustomerWithTotalSpent
{
    public Customer Customer { get; set; }
    public double TotalSpent { get; set; }
}

Then we just have to compile and regenerate T4 templates to have this class in the client side.

Then, we could write our query using this type :

var totalSpentPerCustomer = await (from c in _context.Customers.AsAsyncQueryable()
                                 let totalSpent = (double?)c.Orders.Sum(o =>
                                     o.OrderDetails.Sum(od => od.UnitPrice * od.Quantity * (1 - od.Discount)))
                                   select new CustomerWithTotalSpent { Customer = c, TotalSpent = totalSpent ?? 0 })
                                  .ExecuteAsync();

How does serialization work even though we didn’t use DataContract and DataMember attributes?

In fact, the CustomerWithTotalSpent class we wrote won’t be used by WAQS code. It’s just a model for code generation to generate a new class in the server and another one in the client with DataContract / DataMember attributes:

[System.Runtime.Serialization.DataContractAttribute(IsReference = true, Namespace = http://Northwind/DTO)]
public partial class CustomerWithTotalSpent
{
    [System.Runtime.Serialization.DataMemberAttribute]
    public Customer Customer
    {
        get;
        set;
    }

    [System.Runtime.Serialization.DataMemberAttribute]
    public double TotalSpent
    {
        get;
        set;
    }
}

With this way, it’s possible to serialize in only once a CustomerWithTotalSpent list.

How to use WAQS?

Previous posts on WAQS:

WAQS: Introduction

 

WAQS could be used in many kinds of applications. Some of my customers already use it for these kinds ones:

  • 3-Tiers application
  • Web application (ASP.NET MVC)
  • Web service

In this post, I will describe 3-Tiers application scenario.

3-Tiers application:

3-Tiers application is the default scenario. WAQS was initially made for it.

Architecture

Layers and dependences

A first choice of WAQS, is to choose a layered architecture, a sane split, often use in real world, in which each layer has its own responsibility. Abstractions between layers reduce coupling for a better flexibility and a better testability.

With this approach, WAQS also reduces dependences with used technologies like Entity Framework or WCF for example.

WAQS architecture

The default architecture of an application using WAQS will be the following (with all dark blue layers generated by WAQS):

image

How to generate the code with WAQS?

As we can see in the previous schema, to use current version of WAQS, you will need to use an edmx. In most cases, it’s the first thing you have to create.

Note that in the current version of WAQS, you must use two ways associations (with the two navigation properties) and include FKs.

 

In addition, note that there is a bug on T4 in VS 2013. It works with VS 2012 but with VS 2013 you have to run all T4 templates in Debug mode to be able to use WAQS.

 

In order to be able to generate the code, you first have to install WAQS visual studio extension for the first WAQS usage (as Administrator).

 

With VS 2012, you can do it in Tools/Extensions and Updates. Then if you search WCFAsyncQueryableServices, you will find the WAQS extension. Install it.

Note that to install it, you have to close all other Visual Studio instances.

After installing it, you have to restart VS.

 

With VS 2013, close all instances of VS and download and install Roslyn End Preview vsix and WAQS vsix.

 

Then, in order to be able to generate the server part, you have to install WCFAsyncQueryableServices.Server NuGet package.

To install this package, you can write the following command in the Package Manager Console:

Install-Package WCFAsyncQueryableServices.Server

Note that for your first usage of each WAQS NuGet package version, you have to do it with administrator rights.

Installation of this package won’t change your project but will add a new PowerShell command on the Package Manager Console.

Then, you can use it to generate WAQS code using WCFAsyncQueryableServicesServer command.

For example:

WCFAsyncQueryableServicesServer '"C:\VS Projects\WAQSDemo\WAQSDemo.Web\Northwind.edmx"' All Web

Note that you have the intellisense for it. So you can just write WCFA<tab> <tab> <tab> <tab>.

Note that Web is the default value so it’s useless here. You can choose App instead if you want to use a two tiers application (Web for Web.config, App for App.config). 

Furthermore you have several other options than All. We will talk about it in a future post.

 

Now we have the server code and we will see the client tier.

 

For the client tier, we have a special WPF version and a PCL one (with few restrictions) which is usable in many projects like W8, WP8, but also with iOS and Android via Xamarin (some of my customers did it). Note that today WCF is not completely supported by Xamarin and so you have many restrictions in WAQS with it but, anyway, WAQS could be used in this applications too.

WPF

For the client side, it’s the same way than for the server one.

So, first, install the NuGet package:

Install-Package WCFAsyncQueryableServices.Client.WPF

Then, select the good project in the Package Manager Console

image

Then, you can execute the client generator new PowerShell command still with intellisense:

WCFAsyncQueryableServicesClientWPF '"C:\VS Projects\WAQSDemo\WAQSDemo.Web\Northwind.edmx"' '"C:\VS Projects\WAQSDemo\WAQSDemo.Web\Northwind.svc"' All

When the generation ends you can create a ViewModel class.

After creating it, you should have a default class like this one:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WAQSDemo.Client.WPF
{
    class MainWindowViewModel
    {
    }
}

The Nuget package also added two other commands: WCFAsyncQueryableServicesGlobalClientWPF we will see in a future post and WCFAsyncQueryableServicesApplyViewModelWPF.

We will use this one (still with intellisense):

WCFAsyncQueryableServicesApplyViewModelWPF "Northwind" '"C:\VS Projects\WAQSDemo\WAQSDemo.Client.WPF\MainWindow.xaml"'

After executing it, the ViewModel is now like this one:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApplication1;
using WpfApplication1.ClientContext;
using WpfApplication1.ClientContext.Interfaces;
using WpfApplication1.ClientContext.Interfaces.Errors;
using WCFAsyncQueryableServices.ClientContext;
using WCFAsyncQueryableServices.ClientContext.Interfaces;
using WCFAsyncQueryableServices.ClientContext.Interfaces.Errors;
using WCFAsyncQueryableServices.ClientContext.Interfaces.Querying;
using WCFAsyncQueryableServices.ComponentModel;
 
namespace WAQSDemo.Client.WPF
{
     public class MainWindowViewModel : ViewModelBase
     {
         private INorthwindClientContext _context;
         public MainWindowViewModel(INorthwindClientContext context): base (context)
         {
             _context = context;
         }
     } }

And the constructor of the MainWindow.xaml.cs is now this one:

public MainWindow(WAQSDemo.Client.WPF.MainWindowViewModel vm)
{
     InitializeComponent();
     DataContext = vm; }

So now, in your ViewModel, you have a field of type INorthwindClientContext that can be used to querying your data or saving changes and many other things we will see in the future.

 

Note that every queries supported by LINQ To Entities is usable in LINQ To WAQS (in fact you can do more with LINQ To WAQS).

LINQ To WAQS is asynchronous. To execute a LINQ To WAQS query, you have to use two methods: AsAsyncQueryable and Execute.

var customers = await (from c in _context.Customers.AsAsyncQueryable()
                       select c).ExecuteAsync();

PCL

With current version of WAQS PCL, you must include SL5 and .NET 4.5 (others are optional like WP8 and W8 but note that WP7 or WP7.5 are not supported).

Then, it’s the same process:

Install-Package WCFAsyncQueryableServices.Client.PCL

WCFAsyncQueryableServicesClientPCL '"C:\VS Projects\WAQSDemo\WAQSDemo.Web\Northwind.edmx"' '"C:\VS Projects\WAQSDemo\WAQSDemo.Web\Northwind.svc"' All

The only one difference is on ApplyViewModel because there is no associated view with PCL:

WCFAsyncQueryableServicesApplyViewModelPCL "Northwind"

Then, you have to write the WCF configuration yourself (note that WCF configuration is generated by WAQS with WPF client). If you have an App.config on your client, you can use it to define WCF configuration or you can only use code. For example, in a W8 app, we can use it with this way:

Add a method to SetContext in the MainPage:

public void SetDataContext(PCLViewModel viewModel)
{
     DataContext = viewModel; }

And in the App.xaml.cs, you can use the following code:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
     Frame rootFrame = Window.Current.Content as Frame;
     if (rootFrame == null)
     {
         rootFrame = new Frame();
         Window.Current.Content = rootFrame;
     }
     if (rootFrame.Content == null)
     {
         if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
         {
             throw new Exception("Failed to create initial page");
         }
         ((MainPage)rootFrame.Content).SetDataContext(GetMainPageDataContext());     }
    Window.Current.Activate(); } private PCLViewModel GetMainPageDataContext() {
     IUnityContainer expressionUnityContainer = new UnityContainer();
     expressionUnityContainer.RegisterType<IExpressionTransformer, ExpressionTransformer>();
     ExpressionTransformerFactory.Factory = () => expressionUnityContainer.Resolve<IExpressionTransformer>();
     IUnityContainer unityContainer = new UnityContainer();
     InitWCFAsyncQueryableServicesModules(unityContainer);
     return unityContainer.Resolve<PCLViewModel>(); } private void InitWCFAsyncQueryableServicesModules(IUnityContainer unityContainer) {
     var binding = new CustomBinding();
     var binaryMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement { MaxSessionSize = int.MaxValue };
     binaryMessageEncodingBindingElement.ReaderQuotas.MaxArrayLength = int.MaxValue;
     binaryMessageEncodingBindingElement.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
     binaryMessageEncodingBindingElement.ReaderQuotas.MaxDepth = int.MaxValue;
     binaryMessageEncodingBindingElement.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
     binaryMessageEncodingBindingElement.ReaderQuotas.MaxStringContentLength = int.MaxValue;
     binding.Elements.Add(binaryMessageEncodingBindingElement);
     var httpBindingElement = new HttpTransportBindingElement { MaxBufferSize = int.MaxValue,
        MaxReceivedMessageSize = int.MaxValue };
     binding.Elements.Add(httpBindingElement);
     var endPointAddress = new EndpointAddress(http://localhost:13117/Northwind.svc);
     unityContainer.RegisterType<INorthwindService, NorthwindServiceClient>(new InjectionConstructor(binding,
        endPointAddress));
     unityContainer.RegisterType<INorthwindClientContext, NorthwindClientContext>(); }

 

If you have any comment / issue, you can use the CodePlex web site: https://waqs.codeplex.com/

WAQS: Introduction

I’m very happy to announce that a beta of WAQS is now available on NuGet.

 

I decided to write many posts to explain how to use WAQS and also the different challenges I fixed to develop WAQS.

I will write two kinds of posts: some posts with titles starting by “How to” to explain how to use WAQS and some others starting by “how does WAQS” or “Why does WAQS” to explain how WAQS works.

So, in next months, I will write about many technologies such as T4, Roslyn, NuGet, Entity Framework, LINQ, PowerShell, VS, MS IL, WCF, Unity, Rx, etc.

 

But before talking about all of this, I wanted to start with an introduction to explain what is WAQS.

 

WAQS is a Framework very useful to speed up development of .Net data centric distributed applications.

More clearly, WAQS is a Framework generator. In this sense, it’s better to say that it’s a « meta Framework ». Indeed, WAQS massively uses meta-programming, using intensively T4 templates, Roslyn and NuGet, in order to build a framework fitted with your domain.

WAQS gives you a better productivity with excellent performances, taking in charge technical aspect using well-tried patterns, as well as flexibility and a robust code for your business rules implementation. In this last point, WAQS approach is an efficient alternative, or why not a supplement, to DDD conception.

You have the choice with WAQS: you can use specific parts or you can use it for all the layers of the application. WAQS is an “Opinionated Framework”: it makes some choices but let the flexibility of your architecture choices.

The WAQS (WCF Async Queryable Services) initial idea was to reproduce Entity Framework ObjectContext features on the client, so having the flexibility and the powerful of remotely querying your data and having a transparent changes tracking in order to be able to persist them, later, in one transaction without thinking about it.

Microsoft tried to solve this with WCF Data Services and WCF RIA Services but these Frameworks have a lot of restrictions that are often crippling.

Comparing to these initiatives and also comparing to Entity Framework, WAQS extends possibilities instead of reducing them and Jacob Reimers wrote on twitter "WAQS is remote EF on steroids".

Today, WAQS has evolved into a more comprehensive code generation solution that abstracts away the technical code in order to let developers focus on value add tasks: screens and business code and Jacob tweeted "WAQS adds the abstraction future languages will have. IMO, the gap from C# to C#+WAQS is like the one from IL to C#". Even if I think it’s too much, I agree with him: Roslyn can be used to add a lot of abstraction in order to improve the way developers work today and WAQS does it.

To resume, WAQS proposes:

  • Querying from the client with an asynchronous way,
  • Changes transparent tracking and persistence of them,
  • A solution to avoid « spaghetti » code and code duplication, particularly for business code,
  • Simplifies MVVM.

With a manager point of view, WAQS allows:

  • To industrialize and homogenize development process with a flexible and customizable solution,
  • To improve a lot team productivity,
  • To increase reliability and improve maintainability of the solution,
  • To reduce risks and costs.

And this is possible

  • Without fundamentally change developer habits, WAQS having been thought for them,
  • Without using a private tool or engine. WAQS only generates some code in the solution just using Microsoft tools in order to ensure the maximum of durability and reducing the risk.