. The research of dynamic code compiling under net

Source: Internet
Author: User
Tags contains copy count interface net reflection tostring
Compilation | Dynamic Not sure where the dynamic code compilation is meaningful? A general situation should help to clarify the problem. If you have to take the data out of a database and put it in another database. You should simply use an SQL statement to select data from the source database and insert it into the target database, which is a piece of cake, isn't it? What if you are copying production data to generate test data and need to change data to ensure that target data is used in future development? You may be building a data transmission system (DTS) or some other transport mechanism, but if you do this more than enough data, it becomes the time you spend creating a data-scrubbing-erase mechanism for copying data every time. You can write an application to process and generate test data, but every time you use it on a different application you will have to change (the application) and create a new algorithm.

into dynamic code compilation. Rather than writing some one-time code, you can create an application with a specific internal operating mechanism to transfer data and use snippets to change the data as it is transmitted. The code snippet will represent each action you need to do on the data. They will be stored as raw text in a database or somewhere else where they can easily be modified. Code snippets are compiled and applied to the data at the same time as they are executed. This will allow you to get a completely different piece of code database so that you can easily recover, modify, and apply it without having to change the root of your application every time.

This is a fairly complicated situation, but it should help you understand some possibilities. Now, let's see how to implement it.

   CodeCompileUnit (Code compilation unit)

To dynamically compile a class, start with a CodeCompileUnit from the System.CodeDom namespace. CodeCompileUnit contains a program graphic. To build the code, you create some support objects and add them to the CodeCompileUnit instance. These objects: The representative should already be in your code, just like the ordinary object you are going to create at design time.

. codenamespace-represents the specified namespace

. codetypedeclaration-representative 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 receives a single parameter and returns a value. The parameter value of the Scriptbody method becomes the entity (body) of the SayHello method. You include your code in a static (static) class that receives the parameters that affect the result, creating the CodeCompileUnit.

public static CodeCompileUnit Createexecutionclass (String typenamespace,
String TypeName,
String scriptbody)
{
Create CodeCompileUnit to contain code
CodeCompileUnit CCU = new CodeCompileUnit (); Allocate the required namespaces
CodeNamespace CNS = new CodeNamespace (typenamespace);
Cns. Imports.Add (New CodeNamespaceImport ("System"));
Ccu. Namespaces.add (CNS); Create a new class declaration
CodeTypeDeclaration ParentClass = new CodeTypeDeclaration (typeName);
Cns. Types.add (ParentClass); Creates a SayHello method that gets a parameter and returns a string
CodeMemberMethod method = new CodeMemberMethod ();
Method. Name = "SayHello";
Method. Attributes = Memberattributes.public;
Codeparameterdeclarationexpression arg = new Codeparameterdeclarationexpression (typeof (String), "InputMessage");
Method. Parameters.Add (ARG);
Method. ReturnType = new CodeTypeReference (typeof (String)); Add code required by method entity
CodeSnippetStatement methodbody =new codesnippetstatement (scriptbody);
Method. Statements.add (MethodBody);
ParentClass.Members.Add (method); return CCU;
}
Codeprovider (code provider)
Now that you have created a CodeCompileUnit containing your code snippet, use it to generate all the source code that is compiled into your dynamic assembly. The following static method first invokes the method from the previous example and uses CSharpCodeProvider to generate all the code:

public static string Generatecode (String TypeNamespace,
String TypeName,
String scriptbody)
{
Call our previous method to create the CodeCompileUnit
CodeCompileUnit CCU = Createexecutionclass (TypeNamespace,
TypeName, Scriptbody); CSharpCodeProvider Provider = new CSharpCodeProvider ();
CodeGeneratorOptions options = new CodeGeneratorOptions ();
Options. Blanklinesbetweenmembers = false;
Options. indentstring = "T"; StringWriter SW = new StringWriter ();
Try
{
Provider. Generatecodefromcompileunit (CCU, SW, Options);
Sw. Flush ();
}
Finally
{
Sw. Close ();
return SW. Getstringbuilder (). ToString ();
}
As an example, use the input values: "Codeguru.dynamiccode", "ScriptType", and "return inputmessage;" Call the Generatecode method to derive the following output:

//---------------------------------------------------------------
<auto-generated>
The code is generated by the tool.
Run-time Version: 2.0.50630.0
Changing this file may result in incorrect (program) actions and will discard the changes if the code is generated again.
</auto-generated>
//---------------------------------------------------------------
Namespace Codeguru.dynamiccode {
Using System;
public class ScriptType {
Public virtual string SayHello (string inputmessage) {
return inputmessage;
}
}
} Compiling in memory

The final step is to get the generated source code and compile it into a current assembly. For this example, you are loading this example into memory rather than a physical file. Executes the current compilation action through a specific programming language provider, which is csharpcodeprovider in this routine. You set any predetermined compilation options and compile the assembly from the source code.

The following example code generates an assembly from the code you have built:

Static Assembly Compileinmemory (string code)
{
CSharpCodeProvider Provider = new CSharpCodeProvider (); CompilerParameters options = new CompilerParameters ();
Options. IncludeDebugInformation = false;
Options. GenerateExecutable = false;
Options. GenerateInMemory = true;
CompilerResults results =provider.compileassemblyfromsource (options, code);
Provider. Dispose ();
Assembly generatedassembly = null;
if (results. Errors.Count = 0)
{
generatedassembly = results.compiledassembly;
}
return generatedassembly;
}
such as assembly A = Compileinmemory (Generatecode (TypeNamespace, TypeName, "return inputmessage;")); The call will generate a new assembly. You may use whatever method you want to replace the "return inputmessage;" To create a predetermined variable to make some concurrent calls.

   Create an instance

You have dynamically generated an assembly and compiled it into memory. The next task is to create an instance of a class from the assembly. This is actually more complicated than it sounds. The assembly you have created exists in memory. There is no reference information about its existence, so you cannot simply create a new instance because they do not solve the problem. Create a class to have all compiled assemblies as a workspace. You will disregard the type to determine the event, so when a type is needed you can use one of your types. Executionhost Sample Code

The following code defines a class named Executionhost that tracks all of your dynamically compiled assemblies:

Using System;
Using System.Collections;
Using System.Reflection;
Namespace Codeguru.codedomsample
{
Class Executionhost
{
Private Hashtable assemblies = null;
Public Executionhost ()
{
assemblies = new Hashtable ();
Response Type resolution event (the type resolution Event) request to intercept it and find our type
AppDomain.CurrentDomain.TypeResolve + = new Resolveeventhandler (currentdomain_typeresolve);
}
Private Assembly Currentdomain_typeresolve (object Sender,resolveeventargs args)
{
Find our assembly for the predetermined type
Assembly a = null;
if (assemblies. ContainsKey (args. Name))
{
A = (Assembly) Assemblies[args. Name];
}
return A;
} public void addassembly (string fulltypename, Assembly a)
{
Assemblies. ADD (Fulltypename, a);
public string Execute (string typefullname, String msg)
{
Attempt to create the type required to trigger the event
Type targettype = Type.GetType (TypeFullName, True, true);
Object target =targettype.assembly.createinstance (typefullname);
Iexecutablemodule m = (iexecutablemodule) target; return M.sayhello (msg);
}
}
}
Namespace Codeguru.codedomsample
{
public interface Iexecutablemodule
{
String SayHello (string inputmessage);
}
}
public static CodeCompileUnit Createexecutionclass (string typenamespace,string typename,string scriptbody)
{
Create CodeCompileUnit to store code
CodeCompileUnit CCU = new CodeCompileUnit (); Assigned to the expected namespace
CodeNamespace CNS = new CodeNamespace (typenamespace);
Cns. Imports.Add (New CodeNamespaceImport ("System"));
Ccu. Namespaces.add (CNS);
Creating a Class
CodeTypeDeclaration ParentClass = new CodeTypeDeclaration (typeName);
Cns. Types.add (ParentClass);
New line-Adds an implementation for the Iexecutablemodule interface
ParentClass.BaseTypes.Add (typeof (CodeGuru.CodeDomSample.IExecutableModule));
Creates a SayHello method that gets a parameter and returns a string
CodeMemberMethod method = new CodeMemberMethod ();
Method. Name = "SayHello";
Method. Attributes = Memberattributes.public;
Codeparameterdeclarationexpression arg = new Codeparameterdeclarationexpression (typeof (String),
"InputMessage");
Method. Parameters.Add (ARG);
Method. ReturnType = new CodeTypeReference (typeof (String));
Add expected code to method entity
CodeSnippetStatement methodbody = new CodeSnippetStatement (scriptbody);
Method. Statements.add (MethodBody);
ParentClass.Members.Add (method);
return CCU;
}
Note the Execute method. It uses reflection to create an instance of a predetermined type. This triggers the _typeresolve event and allows one of your assemblies to be returned, if the returned assembly is added to the executionhost through the Addassembly method.

You also need to be aware of the interface implementations that you add to your dynamic build code. Without it, you will not know how to invoke the intended method. For your generated code, the Iexecutablemodule interface is provided with a recent copy of the Createexecutionclass method of the additional interface added as a base class.

Also, because you have added an interface that now needs to be used internally within the Codeguru.dynamiccode assembly, you must add an interface to the codeguru.codedomsample containing the Iexecutablemodule declaration. Please see the latest compileinmemory copy below:

Static Assembly Compileinmemory (string code)
{
CSharpCodeProvider Provider = new CSharpCodeProvider ();
CompilerParameters options = new CompilerParameters ();
Options. IncludeDebugInformation = false;
Options. GenerateExecutable = false;
Options. GenerateInMemory = true;
New line-Add an interface to the desired assembly
Options. Referencedassemblies.add ("CodeGuru.CodeDomSample.exe");
CompilerResults results = provider.compileassemblyfromsource (options, code);
Provider. Dispose (); Assembly generatedassembly = null;
if (results. Errors.Count = 0)
{
generatedassembly = results.compiledassembly;
return generatedassembly;
}
Now, you can use the following test code to test the End-to-end (end-to-end) process of dynamically generating an assembly and then making a call to the method:

String typenamespace = "Codeguru.dynamiccode";
String typeName = "ScriptType" + guid.newguid (). ToString ("N");
Assembly a = Compileinmemory (Generatecode (TypeNamespace, TypeName, "return inputmessage;"));
Executionhost host = new Executionhost ();
String fulltypename = TypeNamespace + "." + TypeName;
Host. Addassembly (Fulltypename, a);
String test = host. Execute (Fulltypename, "Hello world!");
Each time you generate code, use a GUID to generate a unique object name.

   PostScript

You've read a very basic example that describes a complex topic and the code needed to complete the task. The GUID is added to the type name to ensure its uniqueness, so you can compile and use a variety of different types at your whim without conflicting names. You are free to change the "return InputMessage" method entity into any code you like and try it. You can change it so that all the code about the method entity is stored in a database and then restarted at run time.

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.