Monday, February 23, 2015

Using “Roslyn” to Catch CRM Coding Issues

If you’ve been keeping up on the latest and greatest coming out of the Microsoft Developer Division you’ve probably heard about the .NET Compiler Platform or "Roslyn" which is the project to move the existing C# and Visual Basic .NET compilers from C++ to C# and VB respectively. If you’re interested in how these compilers work you can check out the source code on GitHub.

https://github.com/dotnet/roslyn

*Disclaimer – the .NET Compiler Platform hasn’t shipped yet so anything written here is subject to change*

Outside of being able to change how the compiler works developers are now given the same set of APIs Visual Studio uses to do their own code analysis. Products like ReSharper (which I love btw) give us lots of ways to refactor and write better code which but what about scenarios where you want to look for something really specific – like something only applicable when coding against Dynamics CRM. This post is going to walk through creating your own code diagnostic and code fix to a potential CRM specific coding issue.

Problem: Using attribute names with uppercase letters when using the late-bound style of CRM development using .NET. For people starting out there might be some confusion on which value to use when referring to a specific field when given the choice between the logical name (Name) and the schema name. It’s important because case matters.

image

This is hard to catch because a mistake won’t be caught until runtime.
Example:
Entity account = new Entity("account");
account["name"] = "test";
account["new_MyText"] = "test";
_orgService.Create(account);
When you execute this code you’ll receive an error similar to this:

Additional information: 'account' entity doesn't contain attribute with Name = 'new_MyText'.

Which of course might further confuse people starting out because it certainly looks like the Account entity contains an attribute called ‘new_MyText’. There isn’t a clear indication that the the logical, all lowercase name should have been used instead.

Solution: Use the new code analysis APIs to create a code diagnostic that searches for field names with uppercase letters so that the error is highlighted in Visual Studio prior to compiling and then provide a code fix as a quick means to correct the invalid value.

An indication of the incorrect value:

image

And a proposed fix:

image

I’m not going to cover getting the CTP of Visual Studio 2015 setup, everything you need to know is on the .NET Compiler Platform GitHub homepage. To keep the length of the post down I’m only going to cover the highlights of the code as there will be a link to the project on GitHub at the end. The project templates provided actually contain working code to show you the basics which is extremely helpful as there isn’t an overabundance of documentation or samples to refer to yet. Most of what I’ve come up with has been through trial and error. So my resolution might not be the best way to handle this but it appears to work none the less.

There are a number of different times you can register your code analysis to be executed. I chose to do the evaluation when a syntax node is updated and the node is a BracketedArgumentList. This way after the closing bracket is entered (but not before which is important to the user experience) the analysis is performed and we see the red squiggles denoting a problem. You can refer to this blog post on Visualizing Roslyn Syntax Trees for additional background for some additional information.

One of the tools you install during the initial set up is the Roslyn Syntax Visualizer which you can launch inside Visual Studio to get a look at the hierarchy of the code syntax tree. Selecting a piece of code will show its place in the tree and give you additional information. This is how I was able to determine what type of syntax I needed to be worried about.

image

In the actual analysis portion of the code I’m needing to walk the syntax tree to look at different elements. This is essentially the same as traversing an XML document, you’ll have access to methods like .Parent and .ChildNodes or you can also use LINQ to add query capabilities.

One of the things we need to check is to make sure we are working with a Microsoft.Xrm.Sdk.Entity as we don’t want to flag something like this as being wrong:
Test t = new Test();
t["Prop1"] = "hello";
To do this we’ll need to look at the semantic model. The syntax tree is just the text representing the code and can’t tell us the underlying type of an object. When we access the sematic model we see a representation of the code with additional information about the various symbols contained within it.

Once we know we’re working with the correct type we move on to checking the arguments to see if it contains an uppercase letter which we know will cause an issue later. If we establish there is a problem we can then create a diagnostic to be displayed in Visual Studio.

Here is the stripped down version of the analyzer which only accounts for the attribute assignment in this format:
account["name"] = "test";
But we also know that these are also acceptable ways to assign a value to an attribute:
Entity account = new Entity("account") { ["name"] = "test" };

account.Attributes.Add("name", "test");
In the full sample on GitHub these are dealt with. The first can be handled within the same analyzer as we are still dealing with a BracketedArgumentList. The second uses a separate analyzer as the node I’m looking at is a MemberAccessExpression. This probably could have all be combine but for clarity sake I split them out.

In the code fix it’s easy enough to convert the value to lowercase but the trick here is making the replacement. Syntax trees are immutable so they aren’t able to be modified once they are created. So instead we create a new syntax node using one of the SyntaxFactory methods and create a new syntax node (root) using it via ReplaceNode. This new syntax tree is returned and the code is replaced.

Here is the stripped down version of the code fix:


When you launch the debugger you’ll be presented with an experimental instance of Visual Studio used to test extensions. Here you can work with a project and test out to see if the diagnostic is invoked when it is supposed to be. You can set break points in your original instance of Visual Studio to step through code as needed. This could be the result of everything still not being finalized yet but I noticed that if I already had my test project open in a normal instance of Visual Studio and then opened it again in the experimental instance my diagnostics would not be invoked. Once I closed the normal instance and restarted debugging everything would go back to working as expected. Unit testing is available and included in the project template so that instead of debugging against a live project you have the option of using a snippet of code hardcoded into the test. I’ve set up tests in the full sample but won’t be covering them here.

Once complete you’ll end up with a VSIX package you can use for distribution. 

Hopefully this sparks some ideas on some other use cases. I’ve got a few in mind myself that I’ll be looking to put together in the coming months. Good luck!

The full project source code can be downloaded here:

https://github.com/jlattimer/CRMNamingAnalyzer