To WAQS or not to WAQS, that is the question – ASP.NET MVC Patterns comparison

I recently asked to some of my friends who didn’t use WAQS yet why.

I got different replies.

One of them was replied by many of them: I just develop basic ASP.NET MVC applications so I think WAQS is useless / overkill.

 

So I defined with them the following scenario.

Using Northwind, we have five entities: Customer, Order, OrderDetail, Product and Employees.

We want to show the ten best orders per employee with a link to the list of their details.

In this list, we add a column Total and a column CustomerName which is equal to Customer.CompanyName + " – " + Customer.ContactName.

In the order details list, we add a column Amount, a column CustomerName, a column OrderDate and a column ProductName.

 

So how to do it with ASP.NET MVC?

 

Frequent patterns

We have many ways to do it. In my case (with my poor knowledge of ASP.NET MVC), I think about 3 ways:

1. Querying on controllers

For this option, we use some view models that encapsulates our entities:

public class OrderViewModel
{
    public Order Order { get; set; }
    public string CustomerName { get; set; }
    public double Total { get; set; }
} public class OrderDetailViewModel
{
    public OrderDetail OrderDetail { get; set; }
    public string CustomerName { get; set; }
    public DateTime OrderDate { get; set; }
    public string ProductName { get; set; }
    public double Amount { get; set; }
}


Then, we use the following methods in our controller:



public class EmployeeController : Controller
{
    private NorthwindWAQSEntities db = new NorthwindWAQSEntities();

    public async Task<ActionResult> Get10BestOrders(int employeeId)
    {
        return View("Orders",
            await (from o in db.Orders
                   where o.EmployeeId == employeeId
                   let total = o.OrderDetails.Sum(od => od.Quantity * od.UnitPrice * (1 - od.Discount))
                   orderby total descending
                   select new OrderViewModel
                   {
                       Order = o,
                       CustomerName = o.Customer.CompanyName + " " + o.Customer.ContactName,
                       Total = (double?)total ?? 0
                   }).Take(10).ToListAsync());
    }
    public async Task<ActionResult> GetOrderOrderDetails(int orderId)
    {
        return View("OrderDetails",
            await db.OrderDetails.Where(od => od.OrderId == orderId).Select(od =>
                new OrderDetailViewModel
                {
                    OrderDetail = od,
                    CustomerName = od.Order.Customer.CompanyName + " " + od.Order.Customer.ContactName,
                    OrderDate = od.Order.OrderDate,
                    ProductName = od.Product.Name,
                    Amount = od.Quantity * od.UnitPrice * (1 - od.Discount)
                }).ToListAsync());
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            db.Dispose();
        base.Dispose(disposing);
    }
}


It is rather easy even if we have some traps like the fact to think to cast our sum into nullable of double because of SQL logic if an order don’t have any detail.



The worst point IMO is the fact that we have code duplication in our queries which means that if we want to change the way to calculate order detail amount, for example, we have to change all controllers methods using it which is really not great for maintainability.



 



2. In memory calculation



To factorize our code, we can get calculated properties values in memory after loading their entity dependent graph.



To do it, we can use Eager loading with Entity Framework Include method (ASP.NET MVC Scaffolding does it for example), lazy loading or we can use many queries (one per entity type). As I already explained in my blog the two first ones could have a catastrophic impact on performances. But even with the last one, it is not great to load useless data. For example, to get order detail product name, we need to load order details product. But we don’t use product properties except ProductName. So it’s a shame to load them.



 



3. Working with expressions



Another way to factorize our code in our queries is to work with expressions.



So for example, we can use this code for GetOrdersFromEmployeeId:



public async Task<IEnumerable<OrderViewModel>> Get10BestOrders(int employeeId)
{
    return await _context.Orders
.Select(GetOrderViewModelFromOrderExpression ())
.OrderByDescending(o => o.Total)
.Where(o => o.Order.EmployeeId == employeeId)
.Take(10)
.ToListAsync();
}
private static Expression<Func<Order, OrderViewModel>> _orderViewModelFromOrderExpression;
internal static Expression<Func<Order, OrderViewModel>> GetOrderViewModelFromOrderExpression ()
{
    if (_orderViewModelFromOrderExpression != null)
        return _orderViewModelFromOrderExpression;
    var oParameter = Expression.Parameter(typeof(Order));
    _orderViewModelFromOrderExpression =
        Expression.Lambda<Func<Order, OrderViewModel>>(
            Expression.MemberInit(
                Expression.New(typeof(OrderViewModel)),
                Expression.Bind(typeof(OrderViewModel).GetProperty("Order"), oParameter),
                Expression.Bind(typeof(OrderViewModel).GetProperty("CustomerName"),
                    GetCustomerNameExpression (GetCustomerName(oParameter))),
                Expression.Bind(typeof(OrderViewModel).GetProperty("Total"),
                    Expression.Coalesce(
                        Expression.Convert(
                            GetTotalExpressionBody(oParameter),
                            typeof(double?)),
                        Expression.Constant(0D, typeof(double))))),
            oParameter);
    return _orderViewModelFromOrderExpression;
}
internal static Expression<Func<Order, double>> GetTotalExpression ()
{
    var oParameter = Expression.Parameter(typeof(Order));
    return Expression.Lambda<Func<Order, double>>(
        GetTotalExpressionBody(oParameter),
        oParameter);
}
internal static Expression GetTotalExpressionBody(Expression oParameter)
{
    return Expression.Call(
        typeof(Enumerable).GetMethods().First(m => m.Name == "Sum" && m.ReturnType == typeof(double) && m.IsGenericMethod).MakeGenericMethod(typeof(OrderDetail)),
            Expression.MakeMemberAccess(
                oParameter,
                typeof(Order).GetProperty("OrderDetails")),
            GetAmountExpression ());
}
internal static Expression GetCustomerName(Expression oParameter)
{
    var customer = Expression.MakeMemberAccess(oParameter, typeof(Order).GetProperty("Customer"));
    return GetCustomerNameExpression (customer);
}
internal static Expression GetCustomerNameExpression (Expression cParameter)
{
    return Expression.Call(
        typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) }),
        Expression.MakeMemberAccess(cParameter, typeof(Customer).GetProperty("CompanyName")),
        Expression.Constant(" - ", typeof(string)),
        Expression.MakeMemberAccess(cParameter, typeof(Customer).GetProperty("ContactName")));
}
internal static Expression<Func<OrderDetail, double>> GetAmountExpression ()
{
    var odParameter = Expression.Parameter(typeof(OrderDetail));
    return Expression.Lambda<Func<OrderDetail, double>>(
        GetAmountExpressionBody(odParameter),
        odParameter);
}
internal static Expression GetAmountExpressionBody(Expression odParameter)
{
    return Expression.Multiply(
        Expression.Convert(
            Expression.MakeMemberAccess(odParameter, typeof(OrderDetail).GetProperty("Quantity")),
            typeof(double)),
        Expression.Multiply(
            Expression.MakeMemberAccess(odParameter, typeof(OrderDetail).GetProperty("UnitPrice")),
            Expression.Subtract(
                Expression.Constant(1D, typeof(double)),
                Expression.MakeMemberAccess(odParameter, typeof(OrderDetail).GetProperty("Discount")))));
}


So the great thing here is the fact that we don’t duplicate our business code and we don’t load useless data but the issue is the fact that most of developers are not familiar with LINQ expressions and so this code will be hard to write and maintain for them.



 



WAQS



So now what about WAQS?



After installing WAQS NuGet server package in our ASP.NET MVC Application and executing the generation command, we define our business code (named “specifications” in WAQS vocabulary):



public static class NorthwindSpecifications
{
    public static string GetCustomerName(this Order o)
    {
        return o.Customer.CompanyName + " " + o.Customer.ContactName;
    }

    public static string GetCustomerName(this OrderDetail od)
    {
        return od.Order.GetCustomerName();
    }

    public static DateTime GetOrderDate(this OrderDetail o)
    {
        return o.Order.OrderDate;
    }

    public static string GetProductName(this OrderDetail od)
    {
        return od.Product.Name;
    }

    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 => od.GetAmount());
    }
}


After building the project, we can regenerate WAQS T4 files. //We can execute UpdateWCFAsyncQueryableServicesServerT4Templates powershell command in Package Manager Console to do it.



Then, we just need to use this code in our controller:



public class EmployeeController : Controller
{
    private NorthwindWAQSService db = NorthwindWAQSServiceL2E.Create<NorthwindWAQSEntities>();

    public async Task<ActionResult> Get10BestOrders(int employeeId)
    {
        return View("Orders",
            await (from o in db.Orders
                   where o.EmployeeId == employeeId
                   orderby o.Total descending
                   select o).Take(10).WithTotal().WithCustomerName().ToListAsync());
    }

    public async Task<ActionResult> GetOrderOrderDetails(int orderId)
    {
        return View("OrderDetails",
            await db.OrderDetails.Where(od => od.OrderId == orderId).WithCustomerName().WithOrderDate().WithProductName().ToListAsync());
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            ((IDisposable)db).Dispose();
        base.Dispose(disposing);
    }
}


So, using WAQS, we only load what we really need with a very basic way, writing our code only once.



 



 



Conclusion



IMHO WAQS is the best option to develop our basic ASP.NET MVC Application.



In addition, note that the more complex our data model and our business code are, the more interesting WAQS is.



 



Hope that helps. You have no more excuse now guys :)

Posted in 16868, 18472 | 2 Comments

WAQS NuGet PowerShell command usage: init ViewModel using Roslyn

If you take a look to WCFAsyncQueryableFunctionsClientWPF.psm1 of WCFAsyncQueryableServices.Client.WPF NuGet package, you can see the PowerShell command used to initialize ViewModel.

After removing error and TFS management, this is the code of the powershell command to initialize the active document (ViewModel) and an optional associated View.

function WCFAsyncQueryableServicesApplyViewModelWPF($edmxName, $xamlFilePath)
{
    $project = ($DTE.Solution.FindProjectItem($DTE.ActiveDocument.FullName)).ContainingProject

    $edmxName = [System.Text.RegularExpressions.Regex]::Match($edmxName, ‘^\"?(.*?)\"?$’).Groups[1].Value
    $xamlFilePath = [System.Text.RegularExpressions.Regex]::Match($xamlFilePath, ‘^\"?(.*?)\"?$’).Groups[1].Value

    $toolsPath = GetToolsPath
    $defaultNamespace = ($project.Properties | ? {$_.Name -eq ‘RootNamespace’}).Value
    $activeDocumentPath = $DTE.ActiveDocument.FullName
    
    $clientVersion = "WPF"
    $toolsPath = Join-Path $toolsPath ("Client." + $clientVersion)
    $exePath = Join-Path $toolsPath "InitViewModel.exe"
    $waqsFilePath = ($DTE.Solution.FindProjectItem((Join-Path ([System.IO.Path]::GetDirectoryName($project.FullName)) (Join-Path ("WAQS." + $edmxName) ($edmxName + ".Client." + $clientVersion + ".waqs")))).Properties | ? {$_.Name -eq "LocalPath"}).Value
    $exeArgs = @(‘"’ + $edmxName + ‘"’, ‘"’ + $defaultNamespace + ‘"’, ‘"’ + $activeDocumentPath +‘"’, ‘"’ + $waqsFilePath + ‘"’, ‘"’ + $xamlFilePath + ‘"’)
    start-process -filepath $exePath -ArgumentList $exeArgs -Wait
}

Register-TabExpansion ‘WCFAsyncQueryableServicesApplyViewModelWPF’ @{ 
‘edmxName’ = { GetProjects | foreach {(GetAllProjectItems $_)} | ?{$_.Name.EndsWith(".edmx")} | select {[System.IO.Path]::GetFileNameWithoutExtension($_.Name)} | select -unique -ExpandProperty * | Sort-Object | foreach {‘"’ + $_ + ‘"’} };
‘xamlFilePath’ = { (GetAllProjectItems (Get-Project)) | ?{$_.Name.EndsWith(".xaml")} | foreach {$_.Properties | ?{$_.Name -eq ‘LocalPath’} | select -ExpandProperty Value} | Sort-Object | foreach {‘"’ + $_ + ‘"’} }
}

Export-ModuleMember WCFAsyncQueryableServicesApplyViewModelWPF

 

Note that, I’m a PowerShell beginner. So my code could probably be written with a better way.

Also note that the GetProjects and GetAllProjectItems (used for Intellisense) are PowerShell commands defined by WAQS.

 

In order to update safely the ViewModel and the View, WAQS uses Roslyn. But I prefer using C# than PowerShell to do it.

So WAQS NuGet package brings a Console Application (coded in C#) and the PowerShell command executes it with arguments.

 

With WAQS logic, you should have a WAQS configuration xml file in the ViewModel project that describes WAQS generation including namespaces. So, using EnvDTE, NuGet PowerShell command determines the path of this file ($waqsFilePath), the project default namespace ($defaultNamespace) (used if configuration file does not set namespaces) and the path of Active document ($activeDocumentPath).

The Console Application read the ViewModel / View file, parse it using SyntaxFactory.ParseCompilationUnit, use a specific CSharpSyntaxRewriter to transform it and write the text associated to the SyntaxTree in the file.

Note that SyntaxNode.ToString method does not generate a code that compiles because spaces are missing. So you have to use mySyntaxNode.NormalizeWhitespace().ToString().

 

See also: How does WAQS code generation work?

Posted in 16868, 17894, 17895, 18440, 8606 | Leave a comment

Extend Visual Studio with NuGet PowerShell commands basis

There are many ways to extend Visual Studio. The most used is to vsix but today, I want to write about NuGet.

In most cases, NuGet is used to add a dll reference or update a config file or things like this.

But today, I’m writing about a way to extend Visual Studio using PowerShell in a NuGet package that WAQS uses.

 

When you want to create a NuGet package, the easiest way is to download, install and use the NuGet Package Explorer.

When you run it, you have this window:

image

So as you can see a cool feature if you want to learn how NuGet works: you can open a package from online feed (So you can open WAQS packages). // when you do it, take care about licensing…

When you create a new package, you have two parts: Package metadata and Package contents.

image

After editing your package metadata (you can name your package “HelloWorld”, you can focus on Package contents.

Using right click or content menu, you can add some folders. In our case, we are interested in Tools folder.

image

In this folder, there are three important files you can have:

  • init.ps1
  • install.ps1
  • uninstall.ps1

image

As you can see you can use init and install. As their names suggest, install script is only run when you install the package and init is used at initialization which means when you install the package but also when you open a solution with such Nuget package is installed.

Note that there is a strange behavior with install.ps1: it only runs if you have a lib folder in your NuGet. So WAQS creates an empty lib folder for it.

If you look at one of WAQS packages, install.ps1 only open WAQS documentation in Visual Studio:

param($installPath, $toolsPath, $package, $project)
$DTE.ItemOperations.Navigate(‘https://waqs.codeplex.com/documentation’)

 

When you want to add PowerShell commands that can be run in VS Package Manager Console, you need to add them in the init.ps1 (in order to have them anytime you open your solution).

 

So first, you will add a new file in your NuGet package: “MyNuGetCommands.psm1”

In this file, you can define some functions that can be exposed in Package Manager Console.

For example imagine the following function:

function Hello($name, $city)
{
    Write-Host (‘Hello ‘ + $name + ‘. See you soon in ‘ + $city + ‘.’)
}

 

To be able to use it in the Package Manager Console, you have to export this function:

Export-ModuleMember Hello

 

Then, you can add intellisense in the Console using Register-TabExpansion command:

Register-TabExpansion ‘Hello’ @{ 
‘name’ = { "MSFTees", "MVPs", "My friends" };
‘city’ = { "Redmond", "Seattle", "Bellevue", "Duvall" };
}  

MyNuGetCommands.psm1 is done.

 

You now have to import your module in the init.ps1 file:

param($installPath, $toolsPath, $package)
Import-Module (Join-Path $toolsPath MyNuGetCommands.psm1)

 

Now you can save your NuGet on a specified folder (ex: C:\NuGet).

 

Then, in Visual Studio, you can add this folder as a NuGet package Source:

image

image

image

Then, you can install the package in your solution.

To do it you can use the wizard or use the Package Manager Console with install-package command:

image 

Then we can use Hello command (with intellisense on tab):

image

image

Here we are. In the next post, we will see a concrete sample of WAQS NuGet PowerShell commands usage.

 

See also: How does WAQS code generation work?

Posted in 17894, 18440, 8606 | 1 Comment

WAQS: CustomAttribute

7 reasons to use WAQS

WAQS documentation

 

Sometimes you want to use many contexts in a service method.

Currently Func<IContext> is not supported in WAQS specifications.

 

Because WAQS generated types are partial, you can extend them. But I think it’s a shame to extend the client context, the client context interface, the WCF service data contract, the WCF service, the service interface and the service (in order to use WAQS architecture).

 

To avoid it, WAQS proposes a Custom attribute.

In Specifications, you define your method with anything as body (it will be ignored) and with Custom attribute.

For example:

[Custom]
public static void Foo()
{
    throw new NotImplementedException();
}


From this, WAQS generates code in each layers.



In the service one (the one that should contain the method body), WAQS generates a call to a method with the same signature with Body as a method name suffix.



public void Foo()
{
    FooBody();
}


This code does not compile because FooBody method is not generated by WAQS.



You wanted a custom implementation, you have to define it yourself.



But with this way, you only have one method to implement.



 



Then, in this method you can use _contextFactory or _serviceFactory class fields to create a new context.

Posted in 16868 | Leave a comment

WAQS: Math.Round

7 reasons to use WAQS

WAQS documentation

 

I recently had a surprise with .NET:

Math.Round(3.5) = 4 (expected) but Math.Round(2.5) = 2.

 

Indeed, by default, Math.Round uses MidpointRounding.ToEven option.

 

However, SQL Round uses the AwayFromZero mode.

So WAQS only uses it with MidpointRounding.AwayFromZero option and uses arithmetic calculation for MidpointRounding.ToEven.

Like this, we have the same behavior when you get calculated properties in memory or in DB.

 

Now the question is: how to generate WAQS code in PCL client when you use MidpointRounding option in specifications?

Indeed, MidpointRounding does not exist in PCL (with .NET 4.5, SL5, W8, WP8).

So WAQS adds it and adds associated Round methods in a MathExtensions class.

Then, when WAQS duplicates the code on the client, it replaces Math.Round calls with MidpointRounding option to calls to MathExtension methods.

 

For querying, MathExtension class has an attribute of type EquivalentTypeAttribute with typeof(Math) as constructor parameter.

With it, when you use it in LINQ To WAQS queries, the query serialization sent to the server uses Math.Round method and so it works fine.

Posted in 16868, 18204, 7671, 7672 | Leave a comment

How to generate a property from an extension method using Roslyn

In WAQS, we have calculated properties that are generated from extension methods.

In this post, we will see how we can generate a property from a method.

 

In my previous post, we saw a basic method to see if a method is a WAQS “Get method”. In fact, WAQS real code is a little bit more complex.

Indeed, if you look at %Program Files (x86)%\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\WCF Async Queryable Services\Templates\Includes (or %Program Files (x86)%\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\WCF Async Queryable Services\Templates\Includes for VS 2012), you can see that there is a class SpecificationsElements.

This class allows to get WAQS specification methods.

So for example, in a T4 you can use this code:

 

<#@ include file="WCFAsyncQueryableServices.VS12.Specifications.V3.ttinclude"#>
<#@ include file="WCFAsyncQueryableServices.Specifications.V68.ttinclude"#>
<#
var edmxElements = EdmxElements.Get(Host, edmxPath);

var specificationsElements = SpecificationsElements.Get(Host, specificationPathes, entitiesPath, serverFxSpecificationsNamespace, serverFxDALInterfacesNamespace, serverFxServiceInterfacesNamespace, serverEntitiesNamespace, edmxElements, SpecificationsElements.Type.Server);

foreach (var getMethod in specificationsElements.GetMethods)
{
#>
<#= getMethod.Identifier.ValueText.SubString(3) #>
<#
}
#>

 

So now we can find all get methods of our specifications.

 

Let’s see how we can get this property:

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


From this extension method:



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


 



For this, we need a CSharpSyntaxRewriter.



In a first step, we will try to have this property:



public string CustomerName
{
    get
    {
        return string.Concat(this.CompanyName, " - ", this.ContactName);
    }
}


 



We can have it visiting our “Get methods” using the following Rewriter:



public class SpecificationRewriter : CSharpSyntaxRewriter
{
    private SemanticModel _semanticModel;

    public SpecificationRewriter(SemanticModel semanticModel)
    {
        _semanticModel = semanticModel;
    }

    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        return SyntaxFactory.PropertyDeclaration(node.ReturnType, SyntaxFactory.Identifier(node.Identifier.ValueText.Substring(3)))
            .AddModifiers(
                SyntaxFactory.Token(SyntaxKind.PublicKeyword))
            .AddAccessorListAccessors(
                SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration,
                    (BlockSyntax)Visit(node.Body)));
    }

    public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
    {
        if (_semanticModel.GetSymbolInfo(node).Symbol is IParameterSymbol)
            return SyntaxFactory.ThisExpression ();
        return node;
    }
}


 



Now, to add the if statement in the get we can complete the VisitMethodDeclaration method like this:



public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
    string propertyName = node.Identifier.ValueText.Substring(3);
    return SyntaxFactory.PropertyDeclaration(node.ReturnType, SyntaxFactory.Identifier(propertyName))
        .AddModifiers(
            SyntaxFactory.Token(SyntaxKind.PublicKeyword))
        .AddAccessorListAccessors(
            SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration,
                SyntaxFactory.Block(
                    new[] { SyntaxFactory.ParseStatement(
string.Format("if (Specifications != null && Specifications.Has{0})return Specifications.{0};", propertyName)) }
                    .Union(((BlockSyntax)Visit(node.Body)).Statements))
));
}


 



See also: How does WAQS code generation work?

Posted in 16402, 16868, 17895 | Leave a comment

Roslyn basis

Syntax Tree

All the code could be represented as a very detailed tree.

image

Using Roslyn, we can get this syntax tree from a document or we can use Parse methods of SyntaxFactory class.

For example, you can use the following code to get a C# file syntax tree:

var solution = MSBuildWorkspace.Create().OpenSolutionAsync(slnPath).Result;
var project = solution.Projects.First(f => f.FilePath == csProjPath);
var document = project.Documents.First(d => d.FilePath == filePath);

var syntaxRoot = document.GetSyntaxRootAsync().Result;


 



Then, this tree can be analyzed using the Visitor pattern.



For C#, Roslyn provides two visitors:



  • CScharpSyntaxVisitor (generic or not) to analyze the code
  • CScharpSyntaxRewriter to change the syntax tree. Note that as the syntax tree is immutable, it means that we will get another one.


public class MySyntaxVisitor : CSharpSyntaxVisitor
{
   
public override void Visit(SyntaxNode node)
    {
       
base.Visit(node);
       
foreach (var childNode in node.ChildNodes())
           
Visit(childNode);
    }
   
public override void VisitCompilationUnit(CompilationUnitSyntax node)
    {
       
base.VisitCompilationUnit(node);
    }
   
public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
    {
       
base.VisitNamespaceDeclaration(node);
    }
   
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
    {
       
base.VisitClassDeclaration(node);
    }
   
public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
       
base.VisitMethodDeclaration(node);
    }
   
public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
    {
       
base.VisitPropertyDeclaration(node);
    } 



    public override void VisitBlock(BlockSyntax node)
    {
       
base.VisitBlock(node);
    }
   
public override void VisitExpressionStatement(ExpressionStatementSyntax node)
    {
       
base.VisitExpressionStatement(node);
    }
   
public override void VisitInvocationExpression(InvocationExpressionSyntax node)
    {
       
base.VisitInvocationExpression(node);
    }
   
public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
    {
       
base.VisitMemberAccessExpression(node);
    }
   
public override void VisitIdentifierName(IdentifierNameSyntax node)
    {
       
base.VisitIdentifierName(node);
    } 
   
//…
}



 



public class MySyntaxRewriter : CSharpSyntaxRewriter
{
   
public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
    {
       
return base.VisitCompilationUnit(node);
    }
   
public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
    {
       
return base.VisitNamespaceDeclaration(node);
    }
   
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
    {
       
return base.VisitClassDeclaration(node);
    }
   
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
       
return base.VisitMethodDeclaration(node);
    }
   
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
    {
       
return base.VisitPropertyDeclaration(node);
    }  
    public override SyntaxNode VisitBlock(BlockSyntax node)
    {
       
return base.VisitBlock(node);
    }
   
public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
    {
       
return base.VisitExpressionStatement(node);
    }
   
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
    {
       
return base.VisitInvocationExpression(node);
    }
   
public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
    {
       
return base.VisitMemberAccessExpression(node);
    }
   
public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
    {
       
return base.VisitIdentifierName(node);
    } 
    //…



}



 



Note that with SyntaxVisitor you have to Visit children yourself. By default, SyntaxVisitor does not.



With SyntaxRewriter you don’t have to. It is does by default. However, with SyntaxRewriter, you can return a SyntaxNode (without calling Visit or base.VisitXXX) if you want to stop Syntax tree modification.



 



SemanticModel and symbols



Syntax tree is not enough to understand the code.



With SemanticModel you can get the symbol associated to the syntax node.



From this symbol, you can



  • Know what is the method called in an InvocationExpressionSyntax
  • Know type information of a TypeSyntax
  • Know the type of an expression
  • Know if two variables are the same
  • Simplify the code to get some information


To get the SemanticModel, you can use the following code:



var semanticModel = document.GetSemanticModelAsync().Result;



 



Then, to get a symbol from a SemanticModel and a SyntaxNode, there are two methods:



  • semanticModel.GetDeclaredSymbol(node) for declaration SyntaxNode (ClassDeclarationSyntax, MethodDeclarationSyntax, PropertyDeclarationSyntax, etc)
  • semanticModel.GetSymbolInfo(node).Symbol for other SyntaxNode


To get the type of an expression, you can use the GetTypeInfo method:



semanticModel.GetTypeInfo(node).Type



 



Sample





Imagine that you want to get all extension methods with only one parameter that does not return void and that starts with Get.



You can get them without SemanticModel like this:



public class SpecificationVisitor : CSharpSyntaxVisitor
{
    private List<MethodDeclarationSyntax> _getMethods;

    public SpecificationVisitor(List<MethodDeclarationSyntax> getMethods)
    {
        _getMethods = getMethods;
    }

    public override void Visit(SyntaxNode node)
    {
        base.Visit(node);
        foreach (var childNode in node.ChildNodes())
            Visit(childNode);
    }

    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        var predefinedReturnType = node.ReturnType as PredefinedTypeSyntax;
        if (node.Identifier.ValueText.StartsWith("Get")
            && node.ParameterList.Parameters.Count == 1
            && node.Modifiers.OfType<SyntaxToken>().Any(m => m.CSharpKind() == SyntaxKind.StaticKeyword)
            && node.ParameterList.Parameters[0].Modifiers.OfType<SyntaxToken>().Any(m => m.CSharpKind() == SyntaxKind.ThisKeyword)
            && predefinedReturnType != null && predefinedReturnType.Keyword.CSharpKind() != SyntaxKind.VoidKeyword)
            _getMethods.Add(node);
    }
}




Note it’s useless to look for method body or properties, etc. So for performance aspect, it would be better to use the following code:



public class SpecificationVisitor : CSharpSyntaxVisitor
{
    private List<MethodDeclarationSyntax> _getMethods;

    public SpecificationVisitor(List<MethodDeclarationSyntax> getMethods)
    {
        _getMethods = getMethods;
    }

    public override void VisitCompilationUnit(CompilationUnitSyntax node)
    {
        foreach (var n in node.ChildNodes().OfType<NamespaceDeclarationSyntax>())
            Visit(n);
    }

    public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
    {
        foreach (var c in node.ChildNodes().OfType<ClassDeclarationSyntax>())
            Visit(c);
    }

    public override void VisitClassDeclaration(ClassDeclarationSyntax node)
    {
        if (node.Modifiers.Any(m => m.CSharpKind() == SyntaxKind.StaticKeyword))
            foreach (var m in node.ChildNodes().OfType<MethodDeclarationSyntax>())
                Visit(m);
    }

    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        var predefinedReturnType = node.ReturnType as PredefinedTypeSyntax;
        if (node.Identifier.ValueText.StartsWith("Get")
            && node.ParameterList.Parameters.Count == 1
            && node.Modifiers.OfType<SyntaxToken>().Any(m => m.CSharpKind() == SyntaxKind.StaticKeyword)
            && node.ParameterList.Parameters[0].Modifiers.OfType<SyntaxToken>().Any(m => m.CSharpKind() == SyntaxKind.ThisKeyword)
            && predefinedReturnType != null && predefinedReturnType.Keyword.CSharpKind() != SyntaxKind.VoidKeyword)
            _getMethods.Add(node);
    }
}


 



With SemanticModel, VisitMethodDeclaration method is easier:



public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
    var methodSymbol = (IMethodSymbol)_semanticModel.GetDeclaredSymbol(node);
    if (methodSymbol.Name.StartsWith("Get")
        && methodSymbol.Parameters.Length == 1
      && methodSymbol.IsExtensionMethod
      && !methodSymbol.ReturnsVoid)
        _getMethods.Add(node); }


So we saw how to basically use Roslyn.



In next posts, I will explain the usage I made with WAQS.



 



See also: How does WAQS code generation work?

Posted in 16402, 17895 | Leave a comment

Entity Data Model classes

Entity Framework provides some classes to describe the Entity Data Model.

This can be very helpful in many cases and particularly for code generation.

image 

image

image

Then, Entity Framework provides a ttinclude file that allows us to use this model instead of parsing xml file (if you use an edmx).

In addition of this, WAQS provides its own ttinclude file that encapsulates EF one in order to make usage easier. // Note that, of course, you can use it without using WAQS.

 

So for example, if you want to write all public entity types name into a generated file, you can use this T4 code:

<#@ template hostspecific="true" language="C#" #>
<#@ include file="WCFAsyncQueryableServices.VS12.Edmx.Utility.V21.ttinclude"#>
<# 
var edmxElements = EdmxElements.Get(Host, edmxPath);
foreach (var entityType in edmxElements.EntityTypes)
{
    if (Accessibility.ForType(entityType) == "public")
    {
#>
<#= entityType.Name #>
<#
    }
}
#>

 

See also: How does WAQS code generation work?

Posted in 10511, 16868, 17895, 7674, 7675 | Leave a comment

T4 Basis

T4 is a language tags that can be used in Visual Studio to generate file(s) (one file by default).

T4 only has 4 tags:

  • <#@ … #>
  • <# … #>
  • <#= … #>
  • <#+ … #>

Every thing that is not in tags is written as is on the generated file.

 

<#@ … #>

We use this tag in order to define information your meta-code depends on:

<#@ template language="C#" hostspecific="true"#>

language: With T4, you can write your meta-code with C# and VB.NET. So language is useful to specify the language you use.

hostspecific: this is useful to use relative path from the T4 location.

<#@ output extension="…"#>

You have to use it to define default generated file extension (that will have the same name than the T4 file)

<#@ assembly name="…" #>

Use it to add references that you want to use in your meta-code

<#@ import namespace="…"#>

Use it to add usings to your code (for avoiding typing namespaces or to use alias)

<#@ include file="…"#>

This is very used by WAQS. The idea of the include file is to reference another T4 file. So you can share some meta-code between many T4.

When you use include, the T4 engine merge all files together. However, this merge is “stupid”.

Ex:

image

This T4 doesn’t work because T0.ttinclude meta-code is duplicated twice.

So, even if only T1.ttinclude and T2.ttinclude use T0.ttinclude, you have to use this kind of include:

image

This is not cool, particularly when you use an editor with intellisense because when you edit T1.ttinclude file, the editor has no way to know that it has to use T0.ttinclude.

However, sometimes, this is could be useful.

For example, in WAQS, sometimes you have different code generation if you use .NET 4.5 or .NET 4.0 or if you use the PCL client or the WPF one. So in this case, the based .ttinclude can call a method (without knowing it) that is defined in a specific ttinclude file that is referenced by the main T4 file as the based one.

 

<# … #>

This tag contains the meta-code that drives code generation.

If we compare it with a console application, it is the Main method.

 

<#= … #>

This code contains an expression that is evaluated by T4 engine in order to write the result in the generated file.

 

<#+ … #>

This tag allows to define API usable in your meta-code.

You can define anything here such as methods, classes, properties, etc.

However, because T4 engine merge all in one class, you can’t define static classes (so you can’t define extension method for example).

 

 

Sample

image

The above meta-code generates this file:

image

 

Or you can do the same with a .ttinclude that contains all the meta-code as WAQS does:

image

See also: How does WAQS code generation work?

Posted in 10511, 17895 | 1 Comment

WAQS Querying: Math methods

7 reasons to use WAQS

WAQS documentation

 

Math calculation with LINQ To Entities

As with DateTime calculation, Math methods such as Abs, Ceiling, Floor, Pow, Round and Truncate are not supported by LINQ To Entities. With L2E queries, you have to use EntityFunctions methods which is not great.

 

With LINQ To WAQS and LINQ To Entities + WAQS, these methods are supported.

L2WAQS ex:

await _context.Orders.AsAsyncQueryable().Select(o => Math.Round(o.Total, 2)).ExecuteAsync();


 



L2EW ex:



_context.Orders.Select(o => Math.Round(o.Total, 2))






So you can write some queries using Math methods with no reference on Entity Framework.



 



Note that Round method is a little bit more complex than others. More details here.

Posted in 12253, 16868, 7671 | Leave a comment