Skip to content

2011

Roslyn - Formatting Code

Roslyn CTP is the implementation of a 'compiler-as-a-service' for C# and VB.Net. Generally compilers are black boxes, the Roslyn project changes that by opening up APIs that you can use for code related tasks in your tools and applications.

In this post we are investigating source code formatting. Inside my projects I use as much as possible code generation and whether it is coming from T4 templates or some other mechanism, there is usually the need to format your code in a consistent way.

Inside Roslyn we can use the SyntaxTree class that resides in the Roslyn.Compilers.CSharp namespace to parse a text file (that contains C# of VB code). And after we can use the Format extension method that resides in the CompilationUnitSyntax class to reformat your code.

var code = File.ReadAllText("Sample.cs");
var tree = SyntaxTree.ParseCompilationUnit(code);
var root = (CompilationUnitSyntax)tree.Root;
var formattedCode = root.Format().GetFullText();

Take for example the code fragment below which is totally unformatted.

sample.cs
namespace Domain
{
using System;
using System
.Collections
.Generic;
using System.ComponentModel.DataAnnotations;

public class BankAccount
:Entity,
IValidatableObject
{
public BankAccountNumber BankAccountNumber {
get;
set; }

public string Iban
{
get
{
return string
.Format("ES{0} {1} {2} {0}{3}",
this.BankAccountNumber.CheckDigits,
this.BankAccountNumber
.NationalBankCode,
this.BankAccountNumber
.OfficeNumber,
this.BankAccountNumber
.AccountNumber);
}
set {

}
}

public decimal Balance { get;
private set; }

public virtual ICollection
BankAccountActivity
{
get
{
if (_bankAccountActivity
== null)
_bankAccountActivity =
new HashSet();

return _bankAccountActivity;
}
set
{
_bankAccountActivity =
new HashSet(value);
}
}
}
}

When passing the code fragment through the SyntaxTree and calling the Format extension method you get the following result which is formatted correctly.

namespace Domain
{
   using System;
   using System.Collections.Generic;
   using System.ComponentModel.DataAnnotations;

   public class BankAccount : Entity, IValidatableObject
   {
      public BankAccountNumber BankAccountNumber
      {   
         get;
         set;
      }

      public string Iban
      {
         get
         {
            return string.Format("ES{0} {1} {2} {0}{3}", this.BankAccountNumber.CheckDigits, this.BankAccountNumber.NationalBankCode, this.BankAccountNumber.OfficeNumber, this.BankAccountNumber.AccountNumber);
         }
         set
         {
         }
      }

      public decimal Balance
      {
         get;
         private set;
      }

      public virtual ICollection BankAccountActivity
      {
         get
         {
            if (_bankAccountActivity == null)
               _bankAccountActivity = new HashSet();
            return _bankAccountActivity;
         }
         set
         {
            _bankAccountActivity = new HashSet(value);
         }
     }   
   }
}

Note that when you are using for example lambda expressions or delegates that the formatter will add unnecessary newlines and whitespaces. Take for example the following code fragment after calling the Format method.

public void SomeMethod()
{
   this.Click += (s, e) =>
   {
      MessageBox.Show(((MouseEventArgs)e).Location.ToString());
   }
   ;
   treeView.AfterExpand += new TreeViewEventHandler(delegate (object o, TreeViewEventArgs t)
   {
      t.Node.ImageIndex = (int)FolderIconEnum.open;
      t.Node.SelectedImageIndex = (int)FolderIconEnum.open;
   }
);
}

Thankfully we have everything in control through the APIs and we can rewrite the expression (like the Format extension method is doing).

For that we need to create a class inheriting from SyntaxRewriter that resides in the Roslyn.Compilers.CSharp namespace and implements the Visitor pattern.

public class CodeBeautifier: SyntaxRewriter
{
   protected override SyntaxToken VisitToken(SyntaxToken token)
   {
      switch (token.Kind)
      {
         case SyntaxKind.SemicolonToken:

         if (token.GetPreviousToken().Kind == SyntaxKind.CloseBraceToken ||
            token.GetPreviousToken().Kind == SyntaxKind.CloseParenToken)
         {
            return token
            .WithLeadingTrivia()
            .WithTrailingTrivia(Syntax.ElasticCarriageReturnLineFeed);
         }

         break;

      case SyntaxKind.CloseBraceToken:

         if (token.GetNextToken().Kind == SyntaxKind.CloseParenToken ||
            token.GetNextToken().Kind == SyntaxKind.SemicolonToken)
         {
             return token
             .WithTrailingTrivia();
         }

      break;

      case SyntaxKind.CloseParenToken:

         if (token.GetPreviousToken().Kind == SyntaxKind.CloseBraceToken)
         {
            return token
            .WithLeadingTrivia();
         }

      break;
   }

    return token;
}

Note that I am visiting the syntax tokens, these are the terminals of the language grammar (representing the smallest syntactic fragments) and investigating the current kind of token with the next or previous kind of token. Syntax Trivia represents the parts such as whitespace, comments and preprocessor directives. Inside the VisitToken method I am replacing the syntax trivia parts.

To use the CodeBeautifier class you need to simply create an instance of it and using the Visit method to pass your node.

var code = File.ReadAllText("Sample.cs");
var tree = SyntaxTree.ParseCompilationUnit(code);
var root = (CompilationUnitSyntax)tree.Root;
var formattedCode = new CodeBeautifier().Visit(root.Format()).GetFullText();

After the syntax rewriting you will see that the code now looks like below

public void SomeMethod()
{
this.Click += (s, e) =>
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
};
treeView.AfterExpand += new TreeViewEventHandler(delegate (object o, TreeViewEventArgs t)
{
t.Node.ImageIndex = (int)FolderIconEnum.open;
t.Node.SelectedImageIndex = (int)FolderIconEnum.open;
});
}

Windows Live Writer 2011 Tips

Windows Live Writer (WLW) is a great tool that enables you to edit and publish your blog posts. Below you find some tips about WLW.

Make WLW portable

I love portable applications, it enables you to store them on a USB drive or in the cloud without losing your settings and data. It turns out that it’s pretty easy to make WLW portable, and thankfully the investigation I did 3 years ago is still valid for version 2011!

The steps are very simple

  1. Copy all the contents of your installation folder (C:\Program Files (x86)\Windows LiveWriter) to your destination folder (e.g. USB stick)
  2. Create a folder called UserData in your destination folder (same level with the folders Dictionaries, Plugins, …)

You will see after launching WLW and configuring your blog site it will create all necessary files and folders for you settings and posts inside the UserData folder!

wlwuserdatafolder

Edit existing posts

With WLW it’s possible to edit existing posts (that originally were not posted with WLW) on your blog. It’s not directly clear from the user interface how to do that!

Click on the app menu in the ribbon bar en click on Open recent post, not the items that appear on the right!

wlwopenrecentpostmenu

After that you get a dialog where you can choose your blog site on the right. It retrieves all post items of your blog and you simply pick the one that you want to edit!

wlwrecentpostdialog

Use DropBox to sync your local drafts across computers

I use DropBox a lot, it’s a free hosting service that enables you to store and share files and folders across the internet. I use it to share my portable utilities and tools, and also WLW (after making it portable). This way I can always access my drafts and edit them when and where I want!

Introducing Neo4jConnect

I started with an open-source project called Neo4jConnect that enables you to connect to Neo4j, which is a graph-database written in Java and accessible through the REST api. On this page you can find the several operations exposed by Neo4j through REST. Neo4jConnect exposes an object api to manipulate the graph database.

More information and a quickstart can be found on the project site.

In the next couple of weeks I will add more documentation and features!

ArchiMate - Architecture modeling language and tool

ArchiMate is an open and independent Enterprise Architecture modeling language that supports the description, analysis and visualization of architecture within and across business domains. ArchiMate is one of the open standards hosted by The Open Group and is based on the IEEE 1471 standard.

The goals of ArchiMate are

  • To describe architectures and their relations
  • Communicate enterprise architectures with all stakeholders
  • Judge the impact of changes
  • Realise architecture by relating to existing standards, techniques and tools

Archi is a free open source java application to create ArchiMate models.

archi3_800

You can also find a quick reference card of Archimate here and you can also download Archimate stencils for Visio.

Create a .NET Windows Service in 5 steps with Topshelf

Topshelf is an open-source hosting framework for building Windows Services using .NET. With Topshelf you can create in a few lines of code your own windows service. It's a kind of internal DSL for building windows services. I used version 2.2 of Topshelf and the binaries and sources can be found here (GitHub).

First download Topshelf from GitHub, I used version 2.2 (direct link).

  1. Create a console application named SampleWindowsService inside Visual Studio - Be sure to change the target framework to .NET Framework 4
  2. Reference the binaries TopShelf.dll and log4net.dll (included in Topshelf).
  3. Create a simple service called 'SampleService' that simply write every 5 seconds to the log. Note that we create explicit a Start and Stop method which is conceptually the minimum that a windows service need.

    public class SampleService
    {
        private Timer _timer = null;
        readonly ILog _log = LogManager.GetLogger(
                                        typeof(SampleService));
    
        public SampleService()
        {
            double interval = 5000;
            _timer = new Timer(interval);
            _timer.Elapsed += new ElapsedEventHandler(OnTick);
        }
    
        protected virtual void OnTick(object sender, ElapsedEventArgs e)
        {
            _log.Debug("Tick:" + DateTime.Now.ToLongTimeString());
        }
    
        public void Start()
        {
            _log.Info("SampleService is Started");
    
            _timer.AutoReset = true;
            _timer.Enabled = true;
            _timer.Start();
        }
    
        public void Stop()
        {
            _log.Info("SampleService is Stopped");
    
            _timer.AutoReset = false;
            _timer.Enabled = false;
        }
    }
    
  4. In the main method of our console application we will use Topshelf to host our SampleService. We we are telling Topshelf how to start and stop the service, what the service name is, etc. Note that we need to configure log4net for Topshelf and our service!

    static void Main(string[] args)
    {
        XmlConfigurator.ConfigureAndWatch(
            new FileInfo(".\log4net.config"));
    
        var host = HostFactory.New(x =>
        {
            x.EnableDashboard();
            x.Service(s =>
            {
                s.SetServiceName("SampleService");
                s.ConstructUsing(name => new SampleService());
                s.WhenStarted(tc =>
                {
                    XmlConfigurator.ConfigureAndWatch(
                        new FileInfo(".\log4net.config"));
                    tc.Start();
                });
                s.WhenStopped(tc => tc.Stop());
            });
    
            x.RunAsLocalSystem();
            x.SetDescription("SampleService Description");
            x.SetDisplayName("SampleService");
            x.SetServiceName("SampleService");
        });
    
        host.Run();
    }
    
  5. The only thing we have to do now is to configure log4net. Create a file called log4net.config with the following configuration.

    log4net.config
    <?xml version="1.0" encoding="utf-8" ?>
    <log4net>
      <appender name="main" type="log4net.Appender.ConsoleAppender">
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%-5level - %message%newline" />
        </layout>
      </appender>
      <appender name="udp" type="log4net.Appender.UdpAppender">
        <RemoteAddress value="log4view-local"/>
        <RemotePort value="7071"/>
        <layout type="log4net.Layout.XmlLayoutSchemaLog4j"/>
      </appender>
      <root>
        <level value="DEBUG" />
        <appender-ref ref="main" />
        <appender-ref ref="udp" />
      </root>
    </log4net>
    

    This configuration enables to output to the console and through a UDP network protocol so that we can easily monitor the log statements when installed as a windows service. I used Log2Console (Codeplex) to monitor my log statements through UDP.

    Note

    Make sure the output directory of log4net.config is set to Copy always

    Note

    Note that there is an issue with log4net related to IPv6 and Windows Vista/7. You can fix it by adding the following 127.0.0.1 log4view-local to your hosts file which can be found in folder C:\Windows\System32\drivers\etc\hosts.

When you fit F5 you will see that Topshelf outputs some log statements and you will see that the log statements of our SampleService is included and everything is working properly.

TopshelfConsole

In order to install SampleService as a Windows Service you simply need to do the following through the command prompt.

Warning

Be sure to launch the command prompt as an administrator!

SampleWindowsService.exe install

After that when the windows service has been installed successfully we can start the service through services.msc or simply by typing

SampleWindowsService.exe start

Now we can open Log2Console to monitor our log files that is send through the UDP appender.

Log2Console To uninstall the service we simply write

SampleWindowsService.exe uninstall

The sources can be found here (BitBucket)