
Introduction
This is a bit of a strange article, and may not be that useful, but I think
its a very interesting subject, that some will probably not even be aware of.
This article will cover some of the less known namespaces within .NET. Such
as System.CodeDom
and System.CodeDom.Compiler
. What
I will be demonstrating in this article, is just how neat these namespaces are
and what can be done with them. Specifically I will be demonstrating that we
are able to build entirely new source code files at runtime using the System.CodeDom
namespace and the use the System.CodeDom.Compiler
namespace classes
to even compile this newly created source code into a runnable application.
This will all be created at runtime. My aim for this article is to provide a
simple introduction into these 2 namespaces, you will not be an expert at the
end of this article, but will have been exposed to enough, to realise what these
2 namespaces allow you to achieve.
Why Would I Want To Create New Source Code And Applications At Runtime
Well the reason that I started looking into this was dynamic code generation
for Data Access Layers (DAL) /Business Access Layer (BAL) code based on an initial
schema. So thats one possible area where it may be useful to create code at
runtime, basically have an application create a bunch of auto generated source
code files that can then be used within another project. Thats just where we
(at work) found a use for this stuff.
This led me onto investigating the the System.CodeDom.Compiler
namespace classes, and as such I thought I would spend a bit of time talking
about the possibilities that are available by using these classes.
The rest of this article will be split roughly between a discussion about the
System.CodeDom
namespace, and the System.CodeDom.Compiler
namespace. And finally a short discussion of what the attached demo application
will be discussed.
System.CodeDom
The System.CodeDom
namespace, contains classes, interface and
enmerations that can be used to create source code at runtime. The general idea
is that a newly constructed Document Object Model is being created, where methods,
constructors using directives, variables are all creatable via using the System.CodeDom
namespace. Where CodeDom really comes into its own is that it is lanuage agnostic
and simply provides a CodeCompileUnit
which can then be used by
the System.CodeDom.Compiler
namespace which uses this CodeCompileUnit
to create the source code in any of the .NET languages
I shall demonstrate a few examples of using the CodeDom.
Creating Namespaces
CodeNamespace ns = new CodeNamespace("TestApp");
ns.Imports.Add(new CodeNamespaceImport("System"));
ns.Imports.Add(new CodeNamespaceImport("System.Text"));
compileUnit.Namespaces.Add(ns);
Creating A Class
CodeTypeDeclaration ctd = new CodeTypeDeclaration(Name);
ctd.IsClass = true;
ctd.Attributes = MemberAttributes.Public;
compileUnit.Namespaces[0].Types.Add(ctd);
Creating A Main Method
CodeEntryPointMethod method = new CodeEntryPointMethod();
method.Attributes = MemberAttributes.Public |
MemberAttributes.Static;
ctd.Members.Add(method);
Obviously this only gives you a very small example of what CodeDom is like
to work with. I have to say the syntax is very verbose, but the fact that you
can create any .NET source from CodeDom, makes it quite appealing. There are
also some good wrappers around CodeDom, which shorten the syntax, I have included
a list of related articles at the bottom of this article, should you wish to
read around this subject a bit more.
System.CodeDom.Compiler
If we want to be able to use the System.CodeDom.Compiler
namespace
to compile the result of building a CodeDom object, we need to make sure that
wherever we built our CodeDom object (using the syntax as shown above) that
we return a CodeCompileUnit
. It is by using this CodeCompileUnit
that we are able to use the System.CodeDom.Compiler
namespace,
to create the source code in any of the .NET languages.
For example this excerpt of the demo applications code, shows how we built
a new source code file using the System.CodeDom
and System.CodeDom.Compiler
namespaces.
private void picGenerate_Click(object sender, EventArgs e)
{
CodeDomProvider domProvider = (CodeDomProvider)getCodeEditorSyntaxOrProvdiderForLanguage
(cmbPickLanguage.SelectedItem.ToString(),false);
cde.GenerateCode(domProvider, cde.BuildSimpleAdder());
using (StreamReader sr = new StreamReader(getSourceFileName(domProvider)))
{
txtCode.Document.Text = sr.ReadToEnd();
sr.Close();
}
}
....
....
private object getCodeEditorSyntaxOrProvdiderForLanguage(string selectedLanguage, bool syntaxObjectRequired)
{
switch (selectedLanguage)
{
case "CSharp":
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
case "Visual Basic":
if (syntaxObjectRequired)
return SyntaxLanguage.VBNET;
else
return CodeDomProvider.CreateProvider("VisualBasic");
default:
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
}
}
....
....
private string getSourceFileName(CodeDomProvider domProvider)
{
if (domProvider.FileExtension[0] == '.')
{
return "app" + domProvider.FileExtension;
}
else
{
return "app." + domProvider.FileExtension;
}
}
....
....
public CodeCompileUnit BuildSimpleAdder()
{
CodeCompileUnit compileUnit = new CodeCompileUnit();
compileUnit.Namespaces.Add(InitializeNameSpace("TestApp"));
CodeTypeDeclaration ctd = CreateClass("SimplePrint");
compileUnit.Namespaces[0].Types.Add(ctd);
CodeEntryPointMethod mtd = CreateMainMethod();
ctd.Members.Add(mtd);
CodeVariableDeclarationStatement VariableDeclaration =
DeclareVariables(typeof(StringBuilder), "sbMessage");
mtd.Statements.Add(VariableDeclaration);
mtd.Statements.Add(new CodeSnippetExpression(
"Console.WriteLine(sbMessage.Append(\"the result of adding (1+2) is \" + " +
"(1+2).ToString()))"));
CodeTypeReferenceExpression csSystemConsoleType =
new CodeTypeReferenceExpression("System.Console");
CodeMethodInvokeExpression csReadLine =
new CodeMethodInvokeExpression(csSystemConsoleType, "ReadLine");
mtd.Statements.Add(csReadLine);
return compileUnit;
}
....
....
public void GenerateCode(CodeDomProvider domProvider, CodeCompileUnit compileunit)
{
String srcFile;
if (domProvider.FileExtension[0] == '.')
{
srcFile = "app" + domProvider.FileExtension;
}
else
{
srcFile = "app." + domProvider.FileExtension;
}
using (IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(srcFile, false), " "))
{
domProvider.GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions());
tw.Close();
}
}
So doing this is enough to create a new source code file, in any of the .NET
languages. This is all thanks for the CodeDomProvider
class, which
is able to create providers for all of the .NET languages. So providing we use
the the relevant CodeDomProvider
for the CodeCompileUnit
that came out of using CodeDom can be generated into a source code any of the
.NET languages. Neat Huh.
So what have we got so far, well we create a DOM representing a new class,
which eventually gave us a CodeCompileUnit
that we then used to
create the relevant source code by using the specfic e CodeDomProvider
class, for the language we wanted to generate.
So thats cool, we have a source code file. But at the top of this article I
also said that I would show you how to compile source code into an Assembly
or an Exe. So lets continue to look at that.
Again I think the best thing to show you is the relavant source code.
private void picCompile_Click(object sender, EventArgs e)
{
CodeDomProvider domProvider = (CodeDomProvider)getCodeEditorSyntaxOrProvdiderForLanguage
(cmbPickLanguage.SelectedItem.ToString(), false);
string srcFile = getSourceFileName(domProvider);
CompilerResults cr = cde.Compile(domProvider,srcFile,"app.exe");
string message = string.Empty;
if (cr.Errors.Count > 0)
{
message+= "Errors encountered while building " +
srcFile + " into " + cr.PathToAssembly + ": \r\n\n";
foreach (CompilerError ce in cr.Errors)
message +=ce.ToString() + "\r\n";
picRun.Enabled = false;
MessageBox.Show(message);
}
else
{
message += "Source " + srcFile + " built into " +
cr.PathToAssembly + " with no errors.";
picRun.Enabled = true;
MessageBox.Show(message);
}
}
....
....
private object getCodeEditorSyntaxOrProvdiderForLanguage(string selectedLanguage, bool syntaxObjectRequired)
{
switch (selectedLanguage)
{
case "CSharp":
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
case "Visual Basic":
if (syntaxObjectRequired)
return SyntaxLanguage.VBNET;
else
return CodeDomProvider.CreateProvider("VisualBasic");
default:
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
}
}
....
....
private string getSourceFileName(CodeDomProvider domProvider)
{
if (domProvider.FileExtension[0] == '.')
{
return "app" + domProvider.FileExtension;
}
else
{
return "app." + domProvider.FileExtension;
}
}
....
....
public CompilerResults Compile(CodeDomProvider domProvider, String srcFile,String exeFile)
{
String[] referenceAssemblies = { "System.dll" };
CompilerParameters cp = new CompilerParameters(referenceAssemblies,exeFile, false);
cp.GenerateExecutable = true;
CompilerResults compiledResults = domProvider.CompileAssemblyFromFile(cp, srcFile);
return compiledResults;
}
And that is enough to get us a EXE created for the source code that we created earlier. Remember this is all being done at runtime, I think thats
pretty cool actually. Create a new source code and compile and run it at runtime. That's neat.
The DEMO Application
Anyway, lets just have a quick chat about the demo app. I use the fantastic Fireball code highlighter, available right
here at Codeproject.
And the basic program that I am trying to create at runtime is as follows
Namespace TestApp {
using System;
using System.Text;
Public Class SimplePrint {
Public Static void Main() {
System.Text.StringBuilder sbMessage = new System.Text.StringBuilder();
Console.WriteLine(sbMessage.Append("the result of adding (1+2) is " + (1+2).ToString()));
System.Console.ReadLine();
}
}
}
Which when generated in Visual Basic .NET, looks as follows:
Option Strict Off
Option Explicit On
Imports System
Imports System.Text
Namespace TestApp
Public Class SimplePrint
Public Shared Sub Main()
Dim sbMessage As System.Text.StringBuilder = New System.Text.StringBuilder
Console.WriteLine(sbMessage.Append("the result of adding (1+2) is " + (1+2).ToString()))
System.Console.ReadLine
End Sub
End Class
End Namespace
And the demo application looks like this, where there are 3 buttons:
- Generate button : generates the source code
- Compile button : compiles the source code
- Run : runs the newly compiled app
Here is a screen shot of the demo app, just after a generate and compile being
performed, where the selected .NET language was Visual Basic

And here is the actual newly created application runnning

And thats about it. Like I say, you are by no means an expert from reading
this, but I hope its given you an idea about what can be done with these 2 very
cool namespaces.
Other CodeDom Resources
msdn2.microsoft.com/en-us/library/system.codedom.aspx
msdn2.microsoft.com/en-us/library/system.codedom.compiler(VS.71).aspx
http://msdn2.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.aspx
codedom_assistant.aspx
refly.aspx
http://www.codeproject.com/KB/recipes/codedomparser.aspx
http://www.codeproject.com/KB/architecture/codedompatterns.aspx#CodePatternCompileUnit
http://www.codeproject.com/KB/database/ObjectMapping2.aspx
Future Work
I may end up elaborating on this article in the fiture to discuss how the process of
code generation can be handled more efficienly using some of the Visual Studio SDK tools.
History
27/01/08 : Initial Issue