Discover Dynamic Code Compilation

Not sure where dynamic code compilation makes sense? A common scenario should help illustrate the need for it. Suppose you have to pull data out of one database and put it into another. Piece of cake: You'd just use a SQL statement to extract from the source and insert into the destination, right? What if you were copying production data to create test data and needed to alter the data to ensure that the destination data was secure to use in development? You may build a DTS or some other transfer mechanism, but if you do this across enough data it becomes time consuming to build data-scrubbing mechanisms each time you copy data. You could write an application to process and create the test data, but each time you use it on a different application you'll have to alter and create new algorithms.

Enter dynamic code compilation. Rather than continually writing a bunch of throwaway code, you could create an application that has some inner workings to transfer data and apply code snippets to alter the data during the transfer. The code snippets would represent each action you need to take on data. They would be stored as raw text in a database or some other location where they could easily be altered. The code snippets would be compiled and then applied to the data at the time of execution. This would allow you to have a database full of different code snippets that you easily could retrieve, modify, and apply without having to alter your fundamental application each time.

It's a rather complex scenario, but it should help you understand some of the possibilities. Now, look at how to make it happen.

CodeCompileUnit

To dynamically compile a class, start with a CodeCompileUnit from the System.CodeDom namespace. The CodeCompileUnit contains a program graph. To build up the code, you create a number of supporting objects and add them to the CodeCompileUnit instance. The objects represent common things you would have in your code if you were to build it at design time:

CodeNamespace—Represents the assigned namespace

CodeTypeDeclaration—Represents the type declaration

CodeMemberMethod—Represents a method

A HelloWorld Example

You can use the following sample code to generate code that contains a SayHello method that accepts a single parameter and returns a value. The value for the scriptBody method parameter becomes the body for the SayHello method. You'll contain your code to create the CodeCompileUnit within a static class that accepts parameters that influence the result:

CodeProvider

Now that you've created a CodeCompileUnit containing your code snippet, use it to generate the full source code to be compiled into your dynamic assembly. The following static method first calls the method from the previous example and then uses the CSharpCodeProvider to generate the full code:

Discover Dynamic Code Compilation

Compile in Memory

The final step is to take the generated source code and compile it into an actual assembly. For this example, you'll contain the example to memory rather than a physical file. The actual compile action is performed through the specific language provider, which in this case will be the CSharpCodeProvider. You set any desired compile options and then compile the assembly from the source code.

The following sample code generates an assembly from your constructed code:

A call such as Assembly a = CompileInMemory(GenerateCode(typeNamespace, typeName, "return inputMessage;")); would yield a new assembly. You could make subsequent calls with whatever method body you want in place of the "return inputMessage;" to create desired variants.

Creating an Instance

You've dynamically created an assembly and compiled it into memory. The next task is to create an instance of the class from the assembly. This is actually more complicated than it sounds. The assembly you've created exists in memory. No references to it exist, so you can't simply create a new instance because they wouldn't resolve. As a workaround, create a class to hold all of the compiled assemblies. You'll override the type resolution event so that when a type is requested you can use one of your types.

ExecutionHost Sample Code

The following code defines a class named ExecutionHost, which tracks all of your dynamically compiled assemblies:

Notice the Execute method. It uses reflection to create an instance of the desired type. This will trigger the _TypeResolve event and allow one of your assemblies to be returned if it has been added into the ExecutionHost through the AddAssembly method.

Also notice the added interface implementation in your dynamically generated code. Without it, you wouldn't know how to call the desired method. The IExecutableModule interface is provided along with an updated copy of the CreateExecutionClass method that adds the additional interface as a base class for your generated code.

Additionally, because you added an interface that now needs to be used within your CodeGuru.DynamicCode assembly, you must add a reference to the CodeGuru.CodeDomSample that contains the IExecutableModule declaration. See the updated copy of CompileInMemory below:

Using the Guid generates a unique object name each time you generate code.

Possible Enhancements

You've run through a very basic example to demonstrate a complex topic and the code required to accomplish the task. The added Guid in the type name ensures it is unique, so you can compile and use as many different types as you desire without clashing on names. Feel free to alter the "return inputMessage;" method body to whatever code you would like and experiment. You could change it so that all of the code for the method body is stored in a database and retrieved at runtime.

Future Columns

The next column has yet to be determined. If you have something in particular that you would like me to explain, please contact me at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer (MCSD, MCSE, MCDBA) is a senior architect of .NET applications for large and mid-sized organizations. He is a technology leader with Crowe Chizek in Indianapolis, Indiana, specializing in architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the second year in a row. You can reach Mark at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer

Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at mark.strawmyer@crowehorwath.com.

Comments

There are no comments yet. Be the first to comment!

You must have javascript enabled in order to post comments.

Leave a Comment

Your email address will not be published. All fields are required.

Name

Email

Title

Comment

Top White Papers and Webcasts

Data integrity and ultra-high performance dictate the success and growth of many companies.
One of these companies is BridgePay Network Solutions, a recently launched and rapidly growing financial services organization that allows merchants around the world to process millions of daily credit card transactions. Due to the nature of their business, their IT team needed to strike the perfect balance between meeting regulatory-mandated data security measures with the lowest possible levels of latency and …