miércoles, 19 de enero de 2011

Compilación y ejecución dinámica de código .NET


Algunas ocasiones podriamos requerir generar un interprete de scripts o compilar y ejecutar código de forma dinámica, como para el procesamiento de plantillas, una forma de hacer esto es como lo muestra el siguiente listado de código.



Ejemplo de utilización





StringBuilder code = new StringBuilder();
            code.AppendLine ("namespace Sample { ");
                code.AppendLine ("public class Evaluator{");
                    code.AppendLine ("public static double EvalExpression(){");
                    code.AppendLine ("return 3*3;");
                    code.AppendLine ("}");
                code.AppendLine ("}");
            code.AppendLine ("}");

           

            F.Runtime.Compiler compiler = compiler = new F.Runtime.Compiler(F.Runtime.Language.CSharp, code.ToString ());
            
                try
                {
                   

                    compiler.CompilerErrors = new System.CodeDom.Compiler.CompilerErrorCollection();
                    MessageBox.Show(
                        compiler.Execute("Sample", "Evaluator", "EvalExpression", null, true).ToString()
                    );
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            






Listado de código

using Microsoft.VisualBasic;
using Microsoft.CSharp;
using System;
using System.Text;
using System.CodeDom.Compiler;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using System.Collections;

namespace F.Runtime{


    #region Enumerates
    public class Constants
    {
        public static char vbCrLf
        {
            get
            {
                return '\n';
            }
        }
    }
    public enum Language
    {
        VB,
        CSharp
    }
    #endregion

    /// <summary> 
    /// Runtime Dot Net Compiler And Executor 
    /// </summary> 
    /// <remarks></remarks> 
    /// 

    #region Compiler Class
    public class Compiler
    {

        #region Private Members
        private Language m_lang;
        private ArrayList m_dlls;
        private ArrayList m_namespaces;
        private CompilerErrorCollection m_oCompilerErrors;
        private string m_cod;
        private object RunningInstance;
        private MethodInfo TheMethod;
        private string m_userDefFunctions = string.Empty;
        #endregion

        #region Properties
        /// <summary> 
        /// Target dotNet Language to compile 
        /// </summary> 
        /// <value></value> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public Language Language
        {
            get { return m_lang; }
            set { m_lang = value; }
        }
        /// <summary> 
        /// Dlls to be referenced 
        /// </summary> 
        /// <value></value> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public ArrayList Dlls
        {
            get
            {
                if (m_dlls == null)
                {
                    m_dlls = new ArrayList();
                }
                return m_dlls;
            }
            set { m_dlls = value; }
        }
        /// <summary> 
        /// Namespaces to be included 
        /// </summary> 
        /// <value></value> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public ArrayList Namespaces
        {
            get
            {
                if (m_namespaces == null)
                {
                    m_namespaces = new ArrayList();
                }
                return m_namespaces;
            }
            set { m_namespaces = value; }
        }
        /// <summary> 
        /// Dot Net Code to compile 
        /// </summary> 
        /// <value></value> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public string Code
        {
            get { return m_cod; }
            set { m_cod = value; }
        }
        /// <summary> 
        /// Compiler Error Collection 
        /// </summary> 
        /// <value></value> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public CompilerErrorCollection CompilerErrors
        {
            get {
                if (m_oCompilerErrors!=null)
                    m_oCompilerErrors  = new System.CodeDom.Compiler.CompilerErrorCollection();
                return m_oCompilerErrors; }
            set
            {
                m_oCompilerErrors = value;
                try
                {
                    Int16 i;
                    for (i = 0; i <= m_oCompilerErrors.Count - 1; i++)
                    {
                        string[] lines = m_cod.Split(Constants.vbCrLf);
                        if (lines.Length >= m_oCompilerErrors[i].Line)
                        {
                            m_oCompilerErrors[i].ErrorText += lines[m_oCompilerErrors[i].Line - 1]; // ERROR: Unknown assignment operator ConcatString ; 
                        }
                    }
                }
                catch
                {

                }

            }
        }
        public string UserDefinedFunctions
        {
            get { return m_userDefFunctions; }
            set { m_userDefFunctions = value; }
        }
        #endregion

        #region Contructors
        /// <summary> 
        /// Default class constructor 
        /// </summary> 
        /// <remarks></remarks> 
        public Compiler()
            : base()
        {
            m_oCompilerErrors = new CompilerErrorCollection();
            m_lang = Language.VB;
            RunningInstance = null;
            TheMethod = null;

        }
        /// <summary> 
        /// Overloads class constructor taking the dotNet language and code as parameters 
        /// </summary> 
        /// <param name="Lang"></param> 
        /// <param name="theCode"></param> 
        /// <remarks></remarks> 
        public Compiler(Language Lang, string theCode)// ERROR: Unsupported modifier : In, Optional string theCode) : this() 
        {
            m_lang = Lang;
            m_cod = theCode;
        }
        #endregion

        #region Public Methods
        /// <summary> 
        /// 
        /// </summary> 
        /// <param name="counter"></param> 
        /// <param name="paramDef"></param> 
        /// <param name="body"></param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public string EvalDef(Int16 counter, string paramDef, string body)
        {
            string toRet = string.Empty;
            switch (m_lang)
            {
                case Language.CSharp:
                    toRet = Constants.vbCrLf + "public object Eval" + counter + " (" + paramDef + ") {" + Constants.vbCrLf + body + Constants.vbCrLf + "}" + Constants.vbCrLf;
                    break;
                case Language.VB:
                    toRet = Constants.vbCrLf + "Public Function Eval" + counter + " (" + paramDef + ") As Object " + Constants.vbCrLf + body + Constants.vbCrLf + "End Function" + Constants.vbCrLf;
                    break;
            }
            return toRet;
        }
        /// <summary> 
        /// Public method to evaluate the code 
        /// </summary> 
        /// <param name="paramDeclarition">Parameter declaration</param> 
        /// <param name="pCode">Function Body Code</param> 
        /// <param name="params">Array of parameters</param> 
        /// <param name="recompile">Flag to do a recompilation</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public object Eval(string paramDeclarition, string pCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) 
        {
            object toRet = null;
            switch (m_lang)
            {
                case Language.CSharp:
                    toRet = EvalCS(paramDeclarition, pCode, @params, recompile);
                    break;
                case Language.VB:
                    toRet = EvalVB(paramDeclarition, pCode, @params, recompile);
                    break;
            }
            return toRet;
        }
        /// <summary> 
        /// Private method to evaluate the VB code 
        /// </summary> 
        /// <param name="paramDeclarition">Parameter declaration</param> 
        /// <param name="vbCode">Function Body VB Code</param> 
        /// <param name="params">Array of parameters</param> 
        /// <param name="recompile">Flag to do a recompilation</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        private object EvalVB(string paramDeclarition, string vbCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) 
        {
            object toRet = null;
            Type IntanceType;
            if (RunningInstance == null | recompile)
            {
                VBCodeProvider oCodeProvider = new VBCodeProvider();

                CompilerParameters compilerParameters = new CompilerParameters();
                CompilerResults compilerResults;
                System.Reflection.Assembly AssemblyInstance;

                try
                {


                    compilerParameters.ReferencedAssemblies.Add("system.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.xml.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.data.dll");

                    if ((m_dlls != null))
                    {
                        foreach (string d in m_dlls)
                        {
                            compilerParameters.ReferencedAssemblies.Add(d);
                        }
                    }


                    compilerParameters.CompilerOptions = "/t:library";
                    compilerParameters.GenerateInMemory = true;


                    StringBuilder sb = new StringBuilder(string.Empty);

                    sb.Append("Imports System" + Constants.vbCrLf);
                    sb.Append("Imports Microsoft.VisualBasic" + Constants.vbCrLf);
                    sb.Append("Imports System.Text" + Constants.vbCrLf);
                    sb.Append("Imports System.Collections" + Constants.vbCrLf);
                    sb.Append("Imports System.Collections.Generic" + Constants.vbCrLf);
                    sb.Append("Imports System.Text.RegularExpressions" + Constants.vbCrLf);
                    sb.Append("Imports System.Xml" + Constants.vbCrLf);
                    sb.Append("Imports System.Data" + Constants.vbCrLf);

                    if ((m_namespaces != null))
                    {
                        foreach (string n in m_namespaces)
                        {

                            sb.Append("Imports " + n + Constants.vbCrLf);


                        }
                    }

                    sb.Append("Namespace CompilerEngine" + Constants.vbCrLf);
                    sb.Append("Public Class RunTimeArtifact " + Constants.vbCrLf);
                    sb.Append(UserDefinedFunctions);
                    sb.Append("Public Function Eval(" + paramDeclarition + ") As Object " + Constants.vbCrLf);
                    sb.Append(vbCode + Constants.vbCrLf);
                    sb.Append("End Function " + Constants.vbCrLf);
                    sb.Append("End Class " + Constants.vbCrLf);
                    sb.Append("End Namespace" + Constants.vbCrLf);

                    m_cod = sb.ToString();
                    this.m_oCompilerErrors.Clear();

                    try
                    {
                        compilerResults = oCodeProvider.CompileAssemblyFromSource(compilerParameters, m_cod);

                        // Check for compile time errors 
                        if (compilerResults.Errors.Count != 0)
                        {

                            this.CompilerErrors = compilerResults.Errors;
                            throw new Exception("Compile Errors");
                        }

                        else
                        {
                            // No Errors On Compile, so continue to process... 

                            AssemblyInstance = compilerResults.CompiledAssembly;
                            RunningInstance = AssemblyInstance.CreateInstance("CompilerEngine.RunTimeArtifact");


                            IntanceType = RunningInstance.GetType();
                            TheMethod = IntanceType.GetMethod("Eval");

                            toRet = TheMethod.Invoke(RunningInstance, @params);


                        }
                    }

                    catch
                    {
                        throw;
                    }
                }

                catch
                {
                    throw;
                }
            }
            else
            {
                if (TheMethod == null)
                {
                    IntanceType = RunningInstance.GetType();
                    TheMethod = IntanceType.GetMethod("Eval");
                }
                toRet = TheMethod.Invoke(RunningInstance, @params);
            }
            return toRet;

        }
        /// <summary> 
        /// Private method to evaluate the CSharp code 
        /// </summary> 
        /// <param name="paramDeclarition">Parameter declaration</param> 
        /// <param name="csCode">Function Body CSharp Code</param> 
        /// <param name="params">Array of parameters</param> 
        /// <param name="recompile">Flag to do a recompilation</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        private object EvalCS(string paramDeclarition, string csCode, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) 
        {
            object toRet = null;
            Type IntanceType;
            if (RunningInstance == null | recompile)
            {
                CSharpCodeProvider oCodeProvider = new CSharpCodeProvider();

                CompilerParameters compilerParameters = new CompilerParameters();
                CompilerResults compilerResults;
                System.Reflection.Assembly AssemblyInstance;

                try
                {


                    compilerParameters.ReferencedAssemblies.Add("system.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.xml.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.data.dll");

                    if ((m_dlls != null))
                    {
                        foreach (string d in m_dlls)
                        {
                            compilerParameters.ReferencedAssemblies.Add(d);
                        }
                    }


                    compilerParameters.CompilerOptions = "/t:library";
                    compilerParameters.GenerateInMemory = true;

                    StringBuilder sb = new StringBuilder(string.Empty);

                    sb.Append("using System;" + Constants.vbCrLf);
                    sb.Append("using Microsoft.CSharp;" + Constants.vbCrLf);
                    sb.Append("using System.Text;" + Constants.vbCrLf);
                    sb.Append("using System.Collections;" + Constants.vbCrLf);
                    sb.Append("using System.Collections.Generic;" + Constants.vbCrLf);
                    sb.Append("using System.Text.RegularExpressions;" + Constants.vbCrLf);
                    sb.Append("using System.Xml;" + Constants.vbCrLf);
                    sb.Append("using System.Data;" + Constants.vbCrLf);

                    if ((m_namespaces != null))
                    {
                        foreach (string n in m_namespaces)
                        {

                            sb.Append("using " + n + ";" + Constants.vbCrLf);

                        }
                    }

                    // Build a little wrapper code, with our passed in code in the middle 
                    sb.Append("namespace CompilerEngine{" + Constants.vbCrLf);
                    sb.Append(UserDefinedFunctions);
                    sb.Append("public class RunTimeArtifact {" + Constants.vbCrLf);
                    sb.Append("public object Eval(" + paramDeclarition + "){" + Constants.vbCrLf);
                    sb.Append(csCode + Constants.vbCrLf);
                    sb.Append("}" + Constants.vbCrLf);
                    sb.Append("}" + Constants.vbCrLf);
                    sb.Append("}" + Constants.vbCrLf);

                    m_cod = sb.ToString();
                    this.m_oCompilerErrors.Clear();
                    try
                    {
                        compilerResults = oCodeProvider.CompileAssemblyFromSource(compilerParameters, m_cod);


                        // Check for compile time errors 
                        if (compilerResults.Errors.Count != 0)
                        {

                            this.CompilerErrors = compilerResults.Errors;



                            throw new Exception("Compile Errors");
                        }

                        else
                        {
                            // No Errors On Compile, so continue to process... 

                            AssemblyInstance = compilerResults.CompiledAssembly;
                            RunningInstance = AssemblyInstance.CreateInstance("CompilerEngine.RunTimeArtifact");


                            IntanceType = RunningInstance.GetType();
                            TheMethod = IntanceType.GetMethod("Eval");

                            toRet = TheMethod.Invoke(RunningInstance, @params);


                        }
                    }

                    catch
                    {
                        throw;
                    }
                }

                catch
                {
                    throw;
                }
            }
            else
            {
                if (TheMethod == null)
                {
                    IntanceType = RunningInstance.GetType();
                    TheMethod = IntanceType.GetMethod("Eval");
                }
                toRet = TheMethod.Invoke(RunningInstance, @params);
            }
            return toRet;

        }
        /// <summary> 
        /// Compiles and Execute a given code and method 
        /// </summary> 
        /// <param name="Namespace">Enclosing namespace</param> 
        /// <param name="ClassName">Class name</param> 
        /// <param name="Method">Method to Execute</param> 
        /// <param name="params">Parameter array</param> 
        /// <param name="recompile">Flag to force a recompilation</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public object Execute(string Namespace, string ClassName, string Method, object[] @params, bool recompile) // ERROR: Unsupported modifier : In, Optional bool recompile) 
        {
            object toRet = null;
            Type IntanceType;
            if (RunningInstance == null | recompile)
            {
                object oCodeProvider = null;

                switch (m_lang)
                {
                    case Language.CSharp:
                        oCodeProvider = new CSharpCodeProvider();
                        break;
                    case Language.VB:
                        oCodeProvider = new VBCodeProvider();
                        break;

                }

                CompilerParameters compilerParameters = new CompilerParameters();
                CompilerResults compilerResults = null;
                System.Reflection.Assembly AssemblyInstance;

                try
                {


                    compilerParameters.ReferencedAssemblies.Add("system.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.xml.dll");
                    compilerParameters.ReferencedAssemblies.Add("system.data.dll");

                    if ((m_dlls != null))
                    {
                        foreach (string d in m_dlls)
                        {
                            if (d.Trim().Length > 0)
                            {
                                compilerParameters.ReferencedAssemblies.Add(d);
                            }
                        }
                    }


                    compilerParameters.CompilerOptions = "/t:library";
                    compilerParameters.GenerateInMemory = true;

                    this.m_oCompilerErrors.Clear();
                    try
                    {


                        switch (m_lang)
                        {
                            case Language.CSharp:
                                compilerResults = ((CSharpCodeProvider)oCodeProvider).CompileAssemblyFromSource(compilerParameters, m_cod);
                                break;
                            case Language.VB:
                                compilerResults = ((VBCodeProvider)oCodeProvider).CompileAssemblyFromSource(compilerParameters, m_cod);
                                break;
                        }


                        // Check for compile time errors 
                        if (compilerResults.Errors.Count != 0)
                        {

                            this.CompilerErrors = compilerResults.Errors;
                            throw new Exception("Compile Errors");
                        }

                        else
                        {
                            // No Errors On Compile, so continue to process... 

                            AssemblyInstance = compilerResults.CompiledAssembly;
                            RunningInstance = AssemblyInstance.CreateInstance(Namespace + "." + ClassName);


                            IntanceType = RunningInstance.GetType();
                            TheMethod = IntanceType.GetMethod(Method);

                            toRet = TheMethod.Invoke(RunningInstance, @params);


                        }
                    }


                    catch
                    {
                        throw;
                    }
                }

                catch
                {
                    throw;
                }
            }
            else
            {
                bool getMethod = false;
                if (TheMethod == null)
                {
                    getMethod = true;
                }
                else if (!TheMethod.Name.ToLower().Equals(Method.ToLower()))
                {
                    getMethod = true;
                }

                if (getMethod)
                {
                    IntanceType = RunningInstance.GetType();
                    TheMethod = IntanceType.GetMethod(Method);
                }

                toRet = TheMethod.Invoke(RunningInstance, @params);
            }
            return toRet;
        }
        /// <summary> 
        /// Code Setter function 
        /// </summary> 
        /// <param name="_dlls">quoted separed list of assemblies, (by E., system.dll,system.xml.dll,system.data.dll,... )</param> 
        /// <param name="_imports">quoted separed list of namespaces to import (by E. System.Data,System.Text,System.XML,System.Collections,...)</param> 
        /// <param name="_code">source code body namespace and inner classes</param> 
        /// <returns></returns> 
        /// <remarks></remarks> 
        public string SetCode(string _dlls, string _imports, string _code)
        {
            string[] dl;
            string[] im;

            m_cod = string.Empty;

            dl = _dlls.Split(',');
            im = _imports.Split(',');
            Dlls.Clear();
            foreach (string s in dl)
            {
                if (s.Trim().Length > 0)
                {
                    Dlls.Add(s);
                }
            }
            switch (m_lang)
            {
                case Language.CSharp:
                    foreach (string s in im)
                    {
                        if (s.Trim().Length > 0)
                        {
                            m_cod += "using " + s + ";\n";// ERROR: Unknown assignment operator ConcatString ; 
                        }
                    }

                    break;
                case Language.VB:
                    foreach (string s in im)
                    {
                        if (s.Trim().Length > 0)
                        {
                            m_cod += "Imports " + s + "\n";// ERROR: Unknown assignment operator ConcatString ; 
                        }
                    }

                    break;

            }


            m_cod += _code;// ERROR: Unknown assignment operator ConcatString ; 

            return m_cod;
        }
        #endregion

    }
    #endregion

}

Transacciones Fiori

  /UI2/CACHE Register service for UI2 cache use /UI2/CACHE_DEL Delete cache entries /UI2/CHIP Chip Registration /UI2/CUST Customizing of UI ...