CodeDomProviders and Compiler Magic

Published 13 April 08 08:05 PM | andersnoras 

Before .NET 3.5 was released the choice of whether you should choose Visual Basic or C# as your programming language was more or less a matter of taste. With the first incarnations of these languages, you could do virtually the same things with both. Visual Basic 9.0 introduced quite a few new interesting features, and the support for XML literals is my favorite. For those unfamiliar with Visual Basic, XML literals allows you to work with XML DOMs using the all so familiar markup syntax. For instance you this code will build a DOM.

Dim myDOM = <SomeElement> _
                <SomeOtherElement withAnAttribute="42"> _
                    The time is <%= DateTime.Now %> _
                </SomeOtherElement> _
            </SomeElement>

Notice that you can have inline code similar to what you’re familiar with from ASP.NET. You can also use XML literals as a replacement for DOM methods to reference elements within the DOM.

Dim someDocument = XDocument.Load("somedocument.xml")
XElement someOtherElement = someDocument.<SomeElement>.<SomeOtherElement>

XML literals can be used in combination with LINQ and other Visual Basic features, and is a powerful tool when working with XML.

A couple of weeks ago I had a discussion with a team who were planning to use IBM’s Process Server and ESB to produce direct marketing campaigns from Excel documents with assorted information like customer names, addresses, key account managers and so forth. Their idea was to write an adapter for the ESB to be able to consume Excel spreadsheets as data sources, convert these into Service Data Objects and use the Process Server’s mapping abilities to transform the Excel documents into XML that can be passed on to our document production service. Can you imagine how loud my “YANGI”-scream was? This was the perfect moment to exploit Visual Basic’s XML literals to fight enterpriceyness, so I decided to spend a few hours to build a light-weight solution for this. The application accepts an Excel spreadsheet and row template, and produces an XML document with the result. The row template is written using Visual Basic’s XML literals and looks similar to this:

<Letter>
    <FirstName><%= row("First Name") %></FirstName>
    <LastName><%= row("Last Name") %></LastName>
    <HasSameLastNameAsSpouse>
        <=% IIf (row("Last Name") = row("Spouse's Last Name"),"Yes","No") %>
    </HasSameLastNameAsSpouse><
    <!-- And so on... -->
</Letter>

I took me about 45 minutes to write the application, but there was one small problem. The row templates wouldn’t compile using Visual Basic’s CodeDomProvider - even if the generated code built perfectly using the Visual Basic compiler. It turned out that the reason for this was that even when using .NET 3.5, the CodeDomProviders default to using version 2.0 of the compilers. If you’re targeting Visual Basic 9.0, you’ll have to specify this when you create a new CodeDomProvider. The MSDN documentation does not mention this at all, and the many ways of getting hold of a CodeDomProvider makes this even more confusing.

CodeDomProvider vbProvider = CodeDomProvider.CreateProvider("vb");

When you use the factory, you can only specify which language you’re targeting, so we’ll have to instantiate the provider via its constructor. The constructors for the VBCodeProvider and the CSCodeProvider accept an undocumented IDictionary as its argument. It turns out that you can pass configuration options in this dictionary. So to create a .NET 3.5 VBCodeProvider you’ll need to this:

CodeDomProvider provider = new VBCodeProvider(
    new Dictionary<string, string>{{"CompilerVersion","v3.5"}});

This is also the case if you’re looking to use the CodeDOM with C# 3.0 code. The only difference is that you’ll need to create a CSCodeProvider rather than the Visual Basic one. Below is the complete code for my super simple “template engine”.

var className = Guid.NewGuid().ToString("n");
var src = GetTemplate(className);            
CodeDomProvider provider = new VBCodeProvider(
    new Dictionary<string, string>{{"CompilerVersion","v3.5"}});
    var compilerParameters=new CompilerParameters {GenerateInMemory = true};
    var compilerResults = provider.CompileAssemblyFromSource(compilerParameters, src);
    if (compilerResults.Errors.HasErrors)
    {
        throw new CompilationException(compilerResults, src);
    }
return Activator.CreateInstance(compilerResults.CompiledAssembly.GetType(className));

Even if it is confusing, it’s still very easy to compile code using these new language features. Just remember that you’ll need to specify some magic strings to get the compiler to apply it’s magic.

Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# ray.kinsella@gmx.net (Ray Kinsella) said on April 15, 2008 3:24 AM:

What does YANGI stand for, I presume is some XP/Agile-ism, I am unfamiliar with. Is it similar to YAGNI (You aren't going to need it) ... :-)

Regards

Ray

# andersnoras said on April 15, 2008 4:18 AM:

@Ray;

Ops. YANGI is a common typo for YAGNI :-)

(Fun fact: I first wrote YAMGI in this reply)

# template for return to work abilities said on July 8, 2008 3:56 PM:

PingBack from http://kareem.onlinevidssite.info/templateforreturntoworkabilities.html

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below