Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Dynamically Executing Code in .NET

Dynamic code execution is a powerful feature that allows applications to be extended with code that is not compiled into the application. Users can customize applications and developers can dynamically update code easily. Learn what it takes to execute code dynamically with the .NET Framework and create a class that simplifies these tasks by wrapping the details of the process in an easy-to-use interface that requires only a few lines of code.


advertisement
xecuting code dynamically at runtime is a powerful tool to allow users to customize applications after deployment. .NET provides all the tools that make it possible to build code on the fly, compile it and run it dynamically.

I come from an xBase background and have been using Visual FoxPro for many years. One of the nice features of xBase is the ability to dynamically execute code in applications. In Visual FoxPro you can execute code from a string simply by calling EXECSCRIPT() or executing a single expression by calling EVALUATE(). In other environments, however, dynamic code execution is considerably more difficult to achieve, especially in true compiled languages that make it impossible to run code on the fly directly. Those tools have to rely on external tools like the Active Scripting control from Microsoft or other third-party parsers.

Dynamic code execution is a powerful tool for extending applications and allowing customization of an application after it has shipped. Plug-ins and other end-user extensibility features almost exclusively rely on the ability to execute code after formal compilation of the application. Scripting engines and template formatting use dynamic code when it's necessary to mix data with the display output. A good example of this is ASP scripting, which basically is a sophisticated script parser that executes code on the fly. (See Sidebar: Assemblies and Namespaces)

.NET provides full control over dynamic code execution natively via the .NET SDK classes. However, the process is not nearly as trivial as it is in Visual FoxPro. It requires a fair amount of code to accomplish something similar and you need to know how .NET loads assemblies into the application. In exchange, .NET provides a lot of flexibility in using dynamic code with full control over the entire process including compilation, error reporting, loading objects, and controlling the environment.

Compiling Code on the Fly
.NET provides powerful access to the IL code generation process through the System.CodeDom.Compiler, Microsoft.Csharp, and Microsoft.VisualBasic namespaces. In these namespaces you'll find the tools that allow you to compile an assembly either to disk or into memory. You also need the Reflection namespace as it contains the tools to invoke an object and its methods once you've compiled the object.

In the following example I'll demonstrate how to execute an arbitrary block of code. The code is free standing and has no dependencies. The process to execute this code dynamically involves the following steps:

  1. Create or read in the code you want to execute as a string.
  2. Wrap the code into fully functional assembly source code, which includes namespace references (using commands), a namespace, and a class that is to be invoked.
  3. Compile the source code into an assembly.
  4. Check for errors on compilation.
  5. Use the assembly reference to create an instance of the object.
  6. Call the specified method on the instance reference returned using Reflection.
  7. Handle any return value from the method call by casting into the proper type.
Figure 1: This sample form demonstrates how to execute code from the top text box dynamically.
The example shown in Listing 1 demonstrates the code to perform these steps. Figure 1 shows an example of the form that utilizes this code. Please note that there's only minimal error handling provided in most code snippets for brevity's sake.

The code begins by creating various objects that are required for compilation. It then uses the CompilerParameters object to add any assembly references required during compilation. These are the physical DLLs that are required and are the equivalent of what you add in the VS.NET project References section. Note that it's very important that every reference is included or you will get compiler errors. This is one of the tricky parts about dynamic code compilation as this step must occur in your application code. Here the Windows Forms assemblies are included to allow using the MessageBox object to display output.

The next step is to generate the complete source code for an assembly. This example makes a few assumptions about the code in that it presets the method parameter and return value signature as:

public object DynamicCode(params object[] Parameters);

So a block of code MUST return a value of type object or null. It can also accept any number of parameters that can be referenced via the Parameters collection. A simple example of a string to execute might be.

string cName = "Rick"; MessageBox.Show("Hello World" + cName); return (object) DateTime.Now;

If you wanted to access parameters dynamically instead you might do this:


string cName = (string) Parameters[0];

Note that you should cast parameters explicitly to the specific type since the object parameter is generic. You can also return any value as long as you cast it to an object type.

This code is now fixed up into an assembly by adding namespace, class, and method headers. The final generated code that gets compiled looks like this:

using System.IO; using System; using System.Windows.Forms; namespace MyNamespace { public class MyClass { public object DynamicCode( params object[] Parameters) { string cName = "Rick"; MessageBox.Show("Hello World" + cName); return (object) DateTime.Now; } } }

This code can now be compiled into an assembly by using the CompileAssemblyFromSource() method of the CodeCompiler. The CompilerResults object receives information about the result. You can retrieve compile errors via the HasErrors property and Error collection. If there were no errors you get a reference to the Assembly in CompiledAssembly property from which you can call CreateInstance() to create a live instance of the MyClass class.

This is where Reflection comes in: Because we've basically created a .NET type on the fly, the object reference and all method access must occur dynamically rather than through direct referencing. The compiler has no idea of the type at compile time, but must delay creation and type info until runtime. So when I called CreateInstance an object of type Object is returned and I have to use Reflection and InvokeMember to call a method on the object indirectly.

The actual call to the object method then proceeds and returns a reference to a generic object type (much like a variant). This type can contain data of any type and I suggest that you immediately cast the return type to an explicit type if possible. Notice also the error handling around the InvokeMember call—this is fairly crucial as it protects the calling application from any runtime errors that occur in the dynamic code.

I've demonstrated this technique by using Visual C# .NET as the dynamic code language here. You can also use Visual Basic by using the Microsoft.VisualBasic namespace and using the VBCodeProvider class instead to instantiate the loCompiler object. Of course, you'd have to change the assembly source code to VB syntax in addition to the actual dynamic code I show here. The class I'll present later provides the ability to execute both C# and VB code by setting a language property.

As I mentioned at the start of this article, .NET provides a lot of functionality and control over the compile and execution process. However, this is a lot of code to have to integrate into an application each time you want to execute dynamic code. To make life easier I've created a class that simplifies the process considerably and aids in handling errors and debugging the code should errors occur.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap