It looks like the TFS team are adding colorization features to the TFS board. Hopefully this will make my colorizer plugin obsolete.
Outlook Add-In for Replicon
5My employer uses Replicon to track hours against projects and tasks. Its web-based user interface is…adequate. Entering time in an online calendar is fine but I already have an electronic calendar open all day: Microsoft Outlook.
I started a project to create an add-in that adds an “Add to Replicon” menu item to the Outlook calendar. The Replicon Repliconnect API is used to get project and task information and to send the timesheet information back to the server.
It’s still a work in progress. The initial code is up here.
Fault-safe WCF Proxy Generator for Service Reference clients
0WCF is great, but if a connection ever throws an exception for any reason it puts the client in the Faulted state and the client has to be aborted and recreated. It’s a pain in the ass since this can happen anywhere a client method is called.
In a previous post, I showed the dynamic generation of a proxy for a WCF channel.
Nowadays, I suspect most people create their clients using “Add Service Reference”. This generates a ClientBase client that suffers from the same problem as raw channels – if there’s ever an exception, you need to recreate the instance.
Following the earlier post, I created code to use the CIL Emit libraries to dynamically generate a fault-tolerant proxy around a given client. If an exception is thrown, the client is recreated with the correct authentication and whatever settings were used to construct it in the first place.
The code is up on github.
Code Generation with Roslyn – If, Else, and Loops
1if statements
This:
MethodDeclarationSyntax method = SF.MethodDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.VoidKeyword ) ),
"MyMethod"
)
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddBodyStatements(
SF.IfStatement(
SF.BinaryExpression(
SyntaxKind.EqualsExpression,
SF.IdentifierName( "_a" ),
SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( "X" ) )
),
SF.Block(
)
)
)
;
Will get you this:
public void MyMethod() {
if (_a == "X") {
}
}
if-else statements
This:
MethodDeclarationSyntax method = SF.MethodDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.VoidKeyword ) ),
"MyMethod"
)
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddBodyStatements(
SF.IfStatement(
SF.BinaryExpression(
SyntaxKind.EqualsExpression,
SF.IdentifierName( "_a" ),
SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( "X" ) )
),
SF.Block(
),
SF.ElseClause(
SF.Token( SyntaxKind.ElseKeyword ),
SF.IfStatement(
SF.BinaryExpression(
SyntaxKind.EqualsExpression,
SF.IdentifierName( "_a" ),
SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( "Y" ) )
),
SF.Block(
),
SF.ElseClause(
SF.Token( SyntaxKind.ElseKeyword ),
SF.Block(
)
)
)
)
)
)
Will get you this:
public void MyMethod() {
if (_a == "X") {
}
else if (_a == "Y") {
}
else {
}
}
for loops
This:
MethodDeclarationSyntax method = SF.MethodDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.VoidKeyword ) ),
"MyMethod"
)
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddBodyStatements(
SF.ForStatement(
SF.VariableDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.IntKeyword ) ),
SF.SeparatedList(new [] {
SF.VariableDeclarator(
SF.Identifier( "i" ),
null,
SF.EqualsValueClause( SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( 0 ) ) )
)
} )
),
SF.SeparatedList<ExpressionSyntax>(),
SF.BinaryExpression(
SyntaxKind.LessThanExpression,
SF.IdentifierName( "i" ),
SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( 10 ) )
),
SF.SeparatedList<ExpressionSyntax>( new [] {
SF.PostfixUnaryExpression(
SyntaxKind.PostIncrementExpression,
SF.IdentifierName( "i" )
)
} ),
SF.Block(
)
)
)
Will get you this:
public void MyMethod() {
for (int i = 0; i < 10; i++) {
}
}
foreach loops
This defines our generic list:
LocalDeclarationStatementSyntax listDeclaration = SF.LocalDeclarationStatement(
SF.TokenList(),
SF.VariableDeclaration(
SF.GenericName(
SF.Identifier( "IList" ),
SF.TypeArgumentList(
SF.SeparatedList<TypeSyntax>( new [] { SF.PredefinedType( SF.Token( SyntaxKind.StringKeyword ) ) } )
)
),
SF.SeparatedList( new [] {
SF.VariableDeclarator(
SF.Identifier( "list" ),
null,
SF.EqualsValueClause(
SF.ObjectCreationExpression(
SF.Token( SyntaxKind.NewKeyword ),
SF.GenericName(
SF.Identifier( "List" ),
SF.TypeArgumentList(
SF.SeparatedList<TypeSyntax>( new [] { SF.PredefinedType( SF.Token( SyntaxKind.StringKeyword ) ) } )
)
),
SF.ArgumentList( SF.SeparatedList<ArgumentSyntax>( new ArgumentSyntax[0] ) ),
null
)
)
)
} )
)
);
And this our “foreach”:
ForEachStatementSyntax forEachStatement = SF.ForEachStatement(
SF.PredefinedType( SF.Token( SyntaxKind.StringKeyword ) ),
SF.Identifier( "item" ),
SF.IdentifierName( "list" ),
SF.Block()
);
Adding both to a method:
MethodDeclarationSyntax method = SF.MethodDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.VoidKeyword ) ),
"MyMethod"
)
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddBodyStatements(
listDeclaration,
forEachStatement
);
Will get you this:
public void MyMethod() {
IList<string> list = new List<string>();
foreach (string item in list) {
}
}
while loops
This is one of the more straightforward ones.
This:
MethodDeclarationSyntax method = SF.MethodDeclaration(
SF.PredefinedType( SF.Token( SyntaxKind.VoidKeyword ) ),
"MyMethod"
)
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddBodyStatements(
SF.WhileStatement(
SF.LiteralExpression( SyntaxKind.TrueLiteralExpression ),
SF.Block(
SF.BreakStatement()
)
)
);
Will get you this:
public void MyMethod() {
while (true) {
break;
}
}
Code Generation with Roslyn – Fields and Properties
0I’m going to give a few more basic examples of code generation with Roslyn. The following assume that you have a ClassGenerationSyntax variable named “@class”. The examples have also shortened SyntaxFactory to SF using:
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
Properties
The code
// Add a property
PropertyDeclarationSyntax @property = SF.PropertyDeclaration( SF.ParseTypeName( "String" ), "MyProperty" )
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) );
// Add a getter
@property = @property.AddAccessorListAccessors(
SF.AccessorDeclaration( SyntaxKind.GetAccessorDeclaration )
.WithSemicolonToken( SF.Token(SyntaxKind.SemicolonToken )
) );
// Add a private setter
@property = @property.AddAccessorListAccessors(
SF.AccessorDeclaration( SyntaxKind.SetAccessorDeclaration )
.AddModifiers( SF.Token( SyntaxKind.PrivateKeyword ) )
.WithSemicolonToken( SF.Token(SyntaxKind.SemicolonToken )
) );
// Add the property to the class
@class = @class.AddMembers( @property );
Produces the code:
public class MyClass {
public String MyProperty { get; private set; }
}
Variables
This adds a simple field to the class:
FieldDeclarationSyntax aField = SF.FieldDeclaration(
SF.VariableDeclaration(
SF.ParseTypeName( "String" ),
SF.SeparatedList(new [] { SF.VariableDeclarator( SF.Identifier( "_a" ) ) } )
) )
.AddModifiers( SF.Token( SyntaxKind.PrivateKeyword ) );
@class = @class.AddMembers( aField );
Generating the code:
private String _a;
This code initializes the field to a new object:
ExpressionSyntax initializationExpression = SF.ObjectCreationExpression(
SF.Token( SyntaxKind.NewKeyword ),
SF.ParseTypeName( "OtherClass" ),
SF.ArgumentList( SF.SeparatedList<ArgumentSyntax>( new [] {
SF.Argument( SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal( 1 ) ) ),
SF.Argument( SF.LiteralExpression( SyntaxKind.StringLiteralExpression, SF.Literal( "abc" ) ) )
} ) ),
null
);
FieldDeclarationSyntax bField = SF.FieldDeclaration(
SF.VariableDeclaration(
SF.ParseTypeName( "OtherClass" ),
SF.SeparatedList(new [] {
SF.VariableDeclarator(
SF.Identifier( "_b" ),
null,
SF.EqualsValueClause( initializationExpression )
)
} )
) )
.AddModifiers( SF.Token( SyntaxKind.PrivateKeyword ) )
.AddModifiers( SF.Token( SyntaxKind.ReadOnlyKeyword ) )
;
@class = @class.AddMembers( bField );
Generating the code:
private readonly OtherClass _b = new OtherClass(1, "abc");
Field-backed Properties
The following code generates a getter and setter backed by a field:
PropertyDeclarationSyntax property =
SF.PropertyDeclaration( SF.ParseTypeName( "String" ), SF.Identifier( "A" ) )
.AddModifiers( SF.Token( SyntaxKind.PublicKeyword ) )
.AddAccessorListAccessors(
SF.AccessorDeclaration(
SyntaxKind.GetAccessorDeclaration,
SF.Block(
SF.List( new [] {
SF.ReturnStatement( SF.IdentifierName( "_a" ) )
} )
)
),
SF.AccessorDeclaration(
SyntaxKind.SetAccessorDeclaration,
SF.Block(
SF.List( new [] {
SF.ExpressionStatement(
SF.BinaryExpression(
SyntaxKind.SimpleAssignmentExpression,
SF.IdentifierName( "_a" ),
SF.IdentifierName( "value" )
)
)
} )
)
)
)
;
Producing the following code:
public String A {
get {
return _a;
}
set {
_a = value;
}
}
The ratio of code to output shows that it would be a lot easier to generate code from scratch with TextWriter.
Formatting C# with Roslyn
2There’s already been a change to Roslyn that made my last bit of code obsolete. CustomWorkspace no longer requires a string in the constructor and is just:
new CustomWorkspace()
I found out how to change the formatting on the output. We can do this:
CustomWorkspace cw = new CustomWorkspace(); OptionSet options = cw.GetOptions(); options = options.WithChangedOption( CSharpFormattingOptions.OpenBracesInNewLineForMethods, false ); options = options.WithChangedOption( CSharpFormattingOptions.OpenBracesInNewLineForTypes, false ); SyntaxNode formattedNode = Formatter.Format( cu, cw, options );
With this change, our output code is now:
using System;
using System.Generic;
namespace MyNamespace {
private partial class MyClass {
}
}
Generating Code with Roslyn
6Roslyn, now known as the .NET Compiler Platform, essentially provides a DOM for C# and Visual Basic. What it means for code generation is that you can construct an object representing your C# class and Roslyn will generate the .cs file.
This is the first in what I hope to be a series of posts showing how to generate particular elements. This will be for my own reference, if nothing else, but some may find it useful.
Preparing your Project
You’ll have to add the following references, probably using NuGet:
- Microsoft.CodeAnalysis
- Microsoft.CodeAnalysis.CSharp
- Microsoft.CodeAnalysis.CSharp.Workspaces
- Microsoft.CodeAnalysis.Workspaces
- Microsoft.CSharp
Just search NuGet for “CodeAnalysis” and you’ll find them.
Generating Classes from Scratch
The first thing I tried to do with Roslyn was to create an empty class from scratch but I couldn’t find any documentation online for how to do it. Every example I saw started by parsing an existing .cs file and making changes to it. So here’s how you do it.
Any file is considered a Compilation Unit. You can declare one like this:
CompilationUnitSyntax cu = SF.CompilationUnit()
And what’s the first thing in a .cs file? The “using”s. Add them like this:
CompilationUnitSyntax cu = SF.CompilationUnit()
.AddUsings( SF.UsingDirective( SF.IdentifierName( "System" ) ) )
.AddUsings( SF.UsingDirective( SF.IdentifierName( "System.Generic" ) ) )
;
The most important thing to note here is that all Roslyn objects are immutable. So saying:
cu.AddUsings( SF.UsingDirective( SF.IdentifierName( "System" ) ) )
Isn’t going to get you anywhere. AddUsings doesn’t change the cu object; It returns a new CompilationUnitSyntax with the change. You would have to write:
cu = cu.AddUsings( SF.UsingDirective( SF.IdentifierName( "System" ) ) )
So in Roslyn examples you’ll often see people chaining all their code together. I avoid it because it becomes unreadable and unmaintainable.
Next we’ll add a namespace:
NamespaceDeclarationSyntax ns = SF.NamespaceDeclaration( SF.IdentifierName( "MyNamespace" ) ); cu = cu.AddMembers( ns );
Now we’ll add our type to the namespace. We’ll declare it as a partial, private class.
ClassDeclarationSyntax c = SF.ClassDeclaration( "MyClass" )
.AddModifiers( SF.Token( SyntaxKind.PrivateKeyword ) )
.AddModifiers( SF.Token( SyntaxKind.PartialKeyword ) )
;
ns = ns.AddMembers( c );
Finally, we’ll write our type out to a file using the SyntaxTree type. This will write to any TextWriter (like StreamWriter). Here I write it out to a StringBuilder. Note that I have to specify a filename anyway (“C:\out.cs”, below). It doesn’t get used.
SyntaxNode formattedNode = Formatter.Format( cu, new CustomWorkspace( "Host" ) );
StringBuilder sb = new StringBuilder();
using ( StringWriter writer = new StringWriter( sb ) ) {
formattedNode.WriteTo( writer );
}
And here’s what our class looks like:
using System;
using System.Generic;
namespace MyNamespace
{
private partial class MyClass
{
}
}
Amazing.
I’ll have to figure out how to do something about the opening braces being on new lines. I don’t like putting opening braces on new lines.
[Edit 2015-08-23: See this update for some changes to Roslyn object names since this was first posted]
Test utilities
0I put some MSTest-based unit testing utilities up on github. So far I have FileAssert and DirectoryAssert classes that compare the contents of, as you would expect, files and directories. I do a lot of code generation and so it’s useful to be able to compare files and directories easily. I did some extra work so that when files and directories are different, the message that comes back is helpful and identifies the lines and character positions that differ. This is something you don’t get when simply comparing strings.
