Tuesday, September 17, 2013

Using Microsoft Roslyn





Microsoft Roslyn is an API which exposes C# compiler as a service or one can say now the whole compiler is exposed in a form of a library which can be included in your project or application, earlier you used to write code in c# and build that code, if it is successful you get the compiled code in the form of assembly, but now you can actually build or compile the code dynamically from within your .Net applications. You can now have Build process information or compile process information which was not available earlier. So now you can pass in your code as string to the API and it will provide you with the code in Intermediate Language (IL) with syntactic and semantic information regarding your code. So the major advantage we get is one can do the Code Analysis with the help of Roslyn.

Roslyn has nothing to do with the CLR, Roslyn can do same for you as any other compiler can do, that is it can compile your code and return it in Intermediate Language which can then be passed to CLR (As we know CLR don't understand any particular language like C# or VB, so the role of every language compiler (.Net Compliant Language) is to produce a code in IL which is very well understood by the CLR)

With introduction of Roslyn (Right now Roslyn is available a Community Technology Preview) now the compilers are no more the blackbox (which earlier takes in the code writhen in managed language and outputs it as an assembly), now one can get the entire Syntax tree of the code in the form of object (API(s) are exposed to get the Syntax tree).

One of the major advantage which I see now is that you can produce code from User interface, it means that suppose your application is in Production environment and now you need to write some code, earlier you need to do the code changes and build the code again but now you can create a User Interface in your application (UI can have a textarea and a button) and you can actually write code on UI and get it compiled at runtime without doing the deployment again. But yes you have to make your application intelligent enough.

Roslyn API(s) are available as NuGet Packages; you can anytime, so one can install it very easily by Package manager.

Here is a sample code showing some of the features of Roslyn C# compiler.

First of all you need Roslyn Libraries which are available as NuGet Package which can be installed with the help of Package Manager in .Net Visual Studio. For Installing a Roslyn libraries, just open the Package manager console and type in the following command:

PM> Install-Package Roslyn.Compilers.Common

Once the installation is successful, create a sample console application or a Web Application as per your need and add the reference to the two libraries, which you have got from the above installation. Two libraries are:

Roslyn.Compilers
Roslyn.Compilers.CSharp

And you are ready to go.

Firstly we will see how to output the syntax tree or the code as the DLL. You can find the details of what the code do as inline comments within the code.
using System;
using System.IO;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;

namespace SampleApplication
{
    class Program
    {
        static void Main(string[] args)
        {

            //Syntax tree is the code you want to compile at runtime
            var syntaxTree = SyntaxTree.ParseText(@"using System;
                class TestClass
                {
                    static void Main()
                    {
                        Console.WriteLine(""Testing Rolyn"");
                        Console.ReadLine();
                    }
                }");

            //Creates the copimlation, Here we are setting that compile it to a dll for the syntax tree defined above, adding references at runtime, here we are adding metadata
            //reference of System library at runtime
            var compilation = Compilation.Create("RoslynSampleApplication.dll",
                references: new[]
                {
                    new MetadataFileReference(typeof(object).Assembly.Location)
                },
                syntaxTrees: new[] { syntaxTree });

            //Here we are getting the Diagnostic results for a particular syntax tree on copilation, so if there are any errors we will get a list of that errors which can be
            //used to show it to user, These errors are just the same as we see in the Error list of Visual Studio.
            var diagnostics = compilation.GetDiagnostics();

            //Here i am prinitng the errors if any
            foreach (var diagnostic in diagnostics)
            {
                //You can also get the line number here on which the error has occurred. Code goes something like this: diagnostic.Location.GetLineSpan(usePreprocessorDirectives: true).StartLinePosition.Line
                Console.WriteLine("Error: {0}", diagnostic.Info.GetMessage());
            }

            //If we want to generate a DLL for the above syntax tree we can emit the compilation to a DLL by the following code.
            EmitResult result;
            using (var file = new FileStream("RoslynSampleApplication.dll"FileMode.Create))
            {
                result = compilation.Emit(file);
            }
        }
    }
}

The sample code I provided above does not have any errors, if you want to see some errors which are caught by the Compilation diagnostic, you can make some invalid changes in the Syntax tree and you can see the errors in the console window.
The DLL created above can now be consumed in the application. So this the way in which you can create DLL(s) at runtime, now let's see how to write a piece of code at runtime, compile it and produce the output:
 
//Syntax tree is the code you want to compile at runtime
            var syntaxTree = SyntaxTree.ParseText(@"using System;
                class TestClass
                {
                    public static string GetWelcomeMessage()
                    {
                        return ""Welcome! Abhishek Jain"";
                    }
                }");

            //Creates the copimlation, Here we are setting that compile it to a dll for the syntax tree defined above, adding references at runtime, here we are adding metadata
            //reference of System library at runtime
            var compilation = Compilation.Create("RoslynSampleApplication.dll",
                options: new CompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary),
                references: new[]
                {
                    new MetadataFileReference(typeof(object).Assembly.Location)
                },
                syntaxTrees: new[] { syntaxTree });

            //Here we are getting the Diagnostic results for a particular syntax tree on copilation, so if there are any errors we will get a list of that errors which can be
            //used to show it to user, These errors are just the same as we see in the Error list of Visual Studio.
            var diagnostics = compilation.GetDiagnostics();

            //Here i am printng the errors if any
            foreach (var diagnostic in diagnostics)
            {
                //var lineSpan = diagnostic.Location.GetLineSpan(usePreprocessorDirectives: true);
                //var startLine = lineSpan.StartLinePosition.Line;
                Console.WriteLine("Error: {0}", diagnostic.Info.GetMessage());
            }

            //Here the compiled code is emitted into memory stream which is used to create a assembly at runtime, and we are using this assembly at runtime only to invoke a
            //method present in one of the class of this assembly.
            Assembly assembly;
            using (var stream = new MemoryStream())
            {
                EmitResult emitResult = compilation.Emit(stream);
                assembly = Assembly.Load(stream.GetBuffer());
            }

            //Type of class is retrieved and for that type, method is retrieved from it using reflection, Method is invoked also by using reflection
            Type testClass = assembly.GetType("TestClass");
            MethodInfo methodInfo = testClass.GetMethod("GetWelcomeMessage");
            string welcomeMessage = methodInfo.Invoke(nullnull).ToString();

            Console.WriteLine(welcomeMessage);
            Console.ReadLine();
So these are the very basic samples, I am also attaching the source code, if needed you can also try at your end.

No comments:

Post a Comment