Describes the dynamic compilation technology in. NET in detail.

Source: Internet
Author: User

The dynamic compilation and execution of the Code is one. NET platform provides us with powerful tools for flexible expansion (of course, in the face of internal developers) complex and incalculable logic, and use some additional code to expand our existing applications. This provides us with another extension method to a large extent (of course, this is not a strict extension, but at least it provides us with a way of thinking ).

Dynamic code execution can be applied to scenarios such as template generation and logical extension. For a simple example, HTML static pages are often the best choice for websites to respond quickly. However, it is difficult for data-driven websites to use static pages, the work of generating html from dynamic pages may be a good application scenario. In addition, some templates can also be used. In addition, this is also the method of writing plug-ins.

The most basic dynamic compilation

. Net provides us with powerful support to achieve all of this. The two namespaces of the main applications are: System. codeDom. compiler and Microsoft. CSharp or Microsoft. visual Basic. Reflection is also required to dynamically execute your code. The principle of dynamic compilation and code execution is to hand over the provided source code to CSharpCodeProvider for compilation (actually, it is no different from CSC). If there is no compilation error, the generated IL code is compiled into a DLL and stored in the memory and loaded into an application domain (current by default) and through reflection to call a method or trigger an event. The reason is that it is a method of writing plug-ins. It is precisely because of this that we can organize and expand our programs and return them to the main program for triggering through pre-defined excuses. The following are basic steps to dynamically compile and execute code:

· The code to be compiled and executed is read and saved as a string

· Declare a CSharpCodeProvider object instance

· Call the CompileAssemblyFromSource method of the CSharpCodeProvider instance to compile

· Use reflection to generate an instance of the generated object (Assembly. CreateInstance)

· Call its Method

The following code snippet contains the complete compilation and execution process:

Copy codeThe Code is as follows:
// Get the code to compile

String strSourceCode = this.txt Source. Text;

 

// 1. Create a new CSharpCodePrivoder instance

CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider ();

 

// 2. Sets the runtime compiling parameters by crating a new CompilerParameters instance

CompilerParameters objCompilerParameters = new CompilerParameters ();

ObjCompilerParameters. ReferencedAssemblies. Add ("System. dll ");

ObjCompilerParameters. ReferencedAssemblies. Add ("System. Windows. Forms. dll ");

ObjCompilerParameters. GenerateInMemory = true;

 

// 3. CompilerResults: Complile the code snippet by calling a method from the provider

CompilerResults cr = objCSharpCodePrivoder. CompileAssemblyFromSource (objCompilerParameters, strSourceCode );

 

If (cr. Errors. HasErrors)

{

String strErrorMsg = cr. Errors. Count. ToString () + "Errors :";

 

For (int x = 0; x <cr. Errors. Count; x ++)

{

StrErrorMsg = strErrorMsg + "\ r \ nLine:" +

Cr. Errors [x]. Line. ToString () + "-" +

Cr. Errors [x]. ErrorText;

}

 

This.txt Result. Text = strErrorMsg;

MessageBox. Show ("There were build erros, please modify your code.", "Compiling Error ");

 

Return;

}

 

// 4. Invoke the method by using Reflection

Assembly objAssembly = cr. CompiledAssembly;

Object objClass = objAssembly. CreateInstance ("Dynamicly. HelloWorld ");

If (objClass = null)

{

This.txt Result. Text = "Error:" + "Couldn't load class .";

Return;

}

 

Object [] objCodeParms = new object [1];

ObjCodeParms [0] = "Allan .";

 

String strResult = (string) objClass. GetType (). InvokeMember (

"GetTime", BindingFlags. InvokeMethod, null, objClass, objCodeParms );

 

This.txt Result. Text = strResult;

It should be explained that, here we set GenerateInMemory to true when passing the compilation parameter, this indicates that the generated DLL will be loaded into the memory (and then referenced into the current application domain by default ). When calling the GetTime method, we need to add a parameter to pass the array of the object type and call it through the InvokeMember of Reflection. When creating an object instance in the generated Assembly, you must note that the namespace used is the real namespace of your input code. The following is the test code we entered (for convenience, all the code is input externally and will not be adjusted during dynamic execution ):

Copy codeThe Code is as follows:
Using System;

Namespace Dynamicly

{

Public class HelloWorld

{

Public string GetTime (string strName)

{

Return "Welcome" + strName + ", Check in at" + System. DateTime. Now. ToString ();

}

}

}

Run the program provided in the attachment to obtain the following results:

Improved Execution Process

Now everything looks good. We can compile the code and load the code into the current application domain to participate in our activities. But do you want to uninstall this program? What about better program control? In addition, when you run this program many times, you will find that the memory usage is large and the memory usage will increase every execution. Do you need to solve this problem? Yes, of course. Otherwise, you will find that this is useless. Some big applications that I need to execute will make my server crzay overwhelmed and go crazy.

To solve this problem, we need to understand the application domain .. NET Application Domain is. NET provides a container for running and carrying an active Process. It isolates the code and data required for running the Process into a small scope, called Application Domain. When an Application is running, Application Domains loads all the assembly/component sets to the current Application domain and calls them as needed. For dynamically generated code/assembly, it seems that there is no way to manage it. Otherwise, we can use the management assembly method provided by Application Domain to dynamically load and remove Assemblies to improve our performance. How to Do This? Add the following steps based on the front:

· Create another Application Domain

· Dynamically create (compile) Code and save it to disk

· Create a public Remote Call Interface

· Create an instance for remotely calling an interface. And access the method through this interface.

In other words, the object is loaded into another AppDomain and called remotely. The so-called remote call is actually called across application domains, so this object (dynamic code) must inherit from the MarshalByRefObject class. To reuse, this interface is mentioned separately in a project and provides a factory to simplify each call operation:
Copy codeThe Code is as follows:
Using System;

Using System. Collections. Generic;

Using System. Linq;

Using System. Text;

Using System. Reflection;

 

Namespace RemoteAccess

{

/// <Summary>

/// Interface that can be run over the remote AppDomain boundary.

/// </Summary>

Public interface IRemoteInterface

{

Object Invoke (string lcMethod, object [] Parameters );

}

 

/// <Summary>

/// Factory class to create objects exposing IRemoteInterface

/// </Summary>

Public class RemoteLoaderFactory: externalbyrefobject

{

Private const BindingFlags bfi = BindingFlags. Instance | BindingFlags. Public | BindingFlags. CreateInstance;

 

Public RemoteLoaderFactory (){}

 

Public IRemoteInterface Create (string assemblyFile, string typeName, object [] constructArgs)

{

Return (IRemoteInterface) Activator. CreateInstanceFrom (

AssemblyFile, typeName, false, bfi, null, constructArgs,

Null, null, null). Unwrap ();

}

}

}

Next, we need to modify the following information:

· Save the compiled DLL to the disk.

· Create another AppDomain.

· Obtain the reference of the IRemoteInterface interface. (Load the generated DLL to an additional AppDomain)

· Call the InvokeMethod method remotely.

· You can use the AppDomain. Unload () method to uninstall an assembly.

The complete code below demonstrates how to apply this solution.

Copy codeThe Code is as follows:
// Get the code to compile

String strSourceCode = this.txt Source. Text;

 

// 1. Create an addtional AppDomain

AppDomainSetup objSetup = new AppDomainSetup ();

ObjSetup. ApplicationBase = AppDomain. CurrentDomain. BaseDirectory;

AppDomain objAppDomain = AppDomain. CreateDomain ("MyAppDomain", null, objSetup );

 

// 1. Create a new CSharpCodePrivoder instance

CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider ();

 

// 2. Sets the runtime compiling parameters by crating a new CompilerParameters instance

CompilerParameters objCompilerParameters = new CompilerParameters ();

ObjCompilerParameters. ReferencedAssemblies. Add ("System. dll ");

ObjCompilerParameters. ReferencedAssemblies. Add ("System. Windows. Forms. dll ");

 

// Load the remote loader interface

ObjCompilerParameters. ReferencedAssemblies. Add ("RemoteAccess. dll ");

 

// Load the resulting assembly into memory

ObjCompilerParameters. GenerateInMemory = false;

ObjCompilerParameters. OutputAssembly = "DynamicalCode. dll ";

 

// 3. CompilerResults: Complile the code snippet by calling a method from the provider

CompilerResults cr = objCSharpCodePrivoder. CompileAssemblyFromSource (objCompilerParameters, strSourceCode );

 

If (cr. Errors. HasErrors)

{

String strErrorMsg = cr. Errors. Count. ToString () + "Errors :";

 

For (int x = 0; x <cr. Errors. Count; x ++)

{

StrErrorMsg = strErrorMsg + "\ r \ nLine:" +

Cr. Errors [x]. Line. ToString () + "-" +

Cr. Errors [x]. ErrorText;

}

 

This.txt Result. Text = strErrorMsg;

MessageBox. Show ("There were build erros, please modify your code.", "Compiling Error ");

 

Return;

}

 

// 4. Invoke the method by using Reflection

RemoteLoaderFactory factory = (RemoteLoaderFactory) objAppDomain. CreateInstance ("RemoteAccess", "RemoteAccess. RemoteLoaderFactory"). Unwrap ();

 

// With help of factory, create a real 'liveclass' instance

Object objObject = factory. Create ("DynamicalCode. dll", "Dynamicly. HelloWorld", null );

 

If (objObject = null)

{

This.txt Result. Text = "Error:" + "Couldn't load class .";

Return;

}

 

// *** Cast object to remote interface, avoid loading type info

IRemoteInterface objRemote = (IRemoteInterface) objObject;

 

Object [] objCodeParms = new object [1];

ObjCodeParms [0] = "Allan .";

 

String strResult = (string) objRemote. Invoke ("GetTime", objCodeParms );

 

This.txt Result. Text = strResult;

 

// Dispose the objects and unload the generated DLLs.

ObjRemote = null;

AppDomain. Unload (objAppDomain );

 

System. IO. File. Delete ("DynamicalCode. dll ");

For the Input Program of the client, we need to inherit from the externalbyrefobject class and the IRemoteInterface interface, and add references to the RemoteAccess assembly. Enter the following:

Copy codeThe Code is as follows:
Using System;

Using System. Reflection;

Using RemoteAccess;

 

Namespace Dynamicly

{

Public class HelloWorld: externalbyrefobject, IRemoteInterface

{

Public object Invoke (string strMethod, object [] Parameters)

{

Return this. GetType (). InvokeMember (strMethod, BindingFlags. InvokeMethod, null, this, Parameters );

}

 

Public string GetTime (string strName)

{

Return "Welcome" + strName + ", Check in at" + System. DateTime. Now. ToString ();

}

}

}

In this way, you can ensure that your program is always in a controllable and consumable process by compiling, loading, and uninstalling the Assembly in a timely manner, and achieve the goal of dynamic compilation, in addition, in different application domains, your programs are more secure and robust.
Download Sample Code: http://xiazai.jb51.net/201311/yuanma/DynamicCompiler (jb51.net).rar

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.