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?

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

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>