简体   繁体   English

在C#中从反射程序集访问调用上下文

[英]Acessing Calling Context from a Reflection Assembly in C#

I have a windows service written in C# that sends emails based on variables in an XML file this includes access to SQL or MySQL databases via connection strings provided in said XML file. 我有一个用C#编写的Windows服务,该服务基于XML文件中的变量发送电子邮件,其中包括通过所述XML文件中提供的连接字符串访问SQL或MySQL数据库。 I also give the ability for what I call "Custom Fields" which evaluate C# or VB.NET code that a user can also put in the XML file the result of the custom code then can be used to generate emails. 我还提供了所谓的“自定义字段”功能,该功能可以评估C#或VB.NET代码,用户也可以将XML代码中的自定义代码结果放入电子邮件中,然后将其用于生成电子邮件。 Now in custom code I want to provide a set of helper functions that a user can call from their code that will retrieve the value for a field which they can then manipulate however they wish. 现在,在自定义代码中,我想提供一组帮助函数,用户可以从其代码中调用这些函数,这些函数将检索字段的值,然后他们可以根据自己的意愿进行操作。

Basically in the XML the user sets up references to each database they want to access then each table within that database and then each field within that table, each are given an id that is '0' (zero) based IE within each database the table id's start from (zero) and within each table again the fields start from '0' (zero) 基本上,在XML中,用户设置对他们要访问的每个数据库的引用,然后对该数据库中的每个表以及该表中的每个字段进行引用,每个字段都被赋予一个ID,该ID是每个数据库中基于IE的“ 0”(零)IE id从(零)开始,在每个表中,字段再次从“ 0”(零)开始

A reference to a specific field is formatted as follows in what I call breadcrumbs: 在我称为面包屑的情况下,对特定字段的引用格式如下:

0,2,6 (Which is Database ID 0, Table ID 2 within Database ID 0, Field ID 6 within Table ID 2 within Database ID 0) 0,2,6(其中数据库ID 0,数据库ID 0中的表ID 2,数据库ID 0中的表ID 2的字段ID 6)

or 要么

0,3,6 (Which is Database ID 0, Table ID 3 within Database ID 0, Field ID 6 within Table ID 3 within Database ID 0) 0,3,6(其中数据库ID 0,数据库ID 0中的表ID 3,数据库ID 0中表ID 3的字段ID 6)

The user can call helper functions within their custom code that will retrieve the value or values as the case may be of any field that they have referenced in their XML file such as: 用户可以在其自定义代码中调用帮助程序函数,该函数将检索一个或多个值(视情况而定),这取决于他们在XML文件中引用的任何字段,例如:

    <customFields>
    <field id="0" language="VB">
                    <name>Member Program Week</name>
                    <code><![CDATA[Dim FullName As String
FullName = MailEngine.GetFieldValue(0,0,1) + &quot; &quot; + MailEngine.GetFieldValue(0,0,1)
Return FullName]]></code>
                </field>
    </customFields>

As you can see in this custom code there are two fields being referenced one with the field breadcrumbs "0,0,1" (Database ID 0, Table ID 0 within Database ID 0, Field ID 1 within Table ID 0 within Database ID 0) and two with the field breadcrumbs "0,0,2" (Database ID 0, Table ID 0 within Database ID 0, Field ID 2 within Table ID 0 within Database ID 0) 如您在此自定义代码中看到的,引用了两个字段,其中一个字段具有面包屑“ 0,0,1”(数据库ID 0,数据库ID 0中的表ID 0,数据库ID 0中表ID 0的字段ID 1 )和两个带有面包屑“ 0,0,2”的字段(数据库ID 0,数据库ID 0中的表ID 0,数据库ID 0中表ID 0的字段ID 2)

These breadcrumbs are references to the schema provided in the XML for example: 这些面包屑是对XML中提供的架构的引用,例如:

...
<databases nextid="1" mainDataTable="0,0">
    <database id="0">
        <name>Fake Company Databse</name>
        <schema>fakeCompany</schema>
        <tables nextid="1">
            <table id="0" primaryKey="member_id" schema="dbo">
                <name>Members</name>
                <schema>members</schema>
                <fields nextid="3">
                    <field id="0">
                        <schema>member_id</schema>
                        <name>Member ID</name>
                    </field>
                    <field id="1">
                        <schema>member_firstname</schema>
                        <name>Member Firstname</name>
                    </field>
                    <field id="2">
                        <schema>member_lastname</schema>
                        <name>Member Lastname</name>
                    </field>
                    <field id="3">
                        <schema>member_email</schema>
                        <name>Member Email</name>
                    </field>
                </fields>
            </table>
        </tables>
    </database>
</databases>
...

Essentially that custom code would create a string variable named "FullName" and then retrieve the First Name and Last Name of a member join them by a " " (space) character and store the result in the "FullName" variable and then return the result. 从本质上讲,该自定义代码将创建一个名为“ FullName”的字符串变量,然后检索成员的名字和姓氏,并通过一个“”(空格)字符将它们连接起来,并将结果存储在“ FullName”变量中,然后返回结果。 The customCode could then be called from within a message as such "C,0" C=Custom. 然后可以从消息中调用customCode,例如“ C,0” C = Custom。

Now here comes my question: 现在是我的问题:

As all the processing of database fields etc is done within the service itself then the custom code is called also from within the service using Reflections how do I, from the helper (MailEngine) class gain access to the functions and variables used by the service seeing that the helper (MailEngine) class is included in the custom code which is in a different context to the service. 由于对数据库字段等的所有处理都是在服务本身中完成的,因此还可以使用Reflections从服务内部调用自定义代码,我该如何从帮助程序(MailEngine)类访问服务所使用的函数和变量,从而看到帮助器(MailEngine)类包含在与服务不同的上下文中的自定义代码中。

For example if in the MailEngine class I have a function as follows 例如,如果在MailEngine类中,我具有如下功能

public static string GetValueForField(int DatabaseID, int TableID, int FieldID)
{

}

What do I need to do so that from within this function or others in this class so that I can call functions or retrieve variables that I have within the service. 我需要做些什么,以便从此函数或此类中的其他函数中进行调用,以便可以调用函数或检索服务中具有的变量。

Is this possible or do I need to re-parse the xml document reconnect to the database recollect all the field values and go from there... 这是可能的还是我需要重新解析xml文档,重新连接到数据库,重新收集所有字段值,然后从那里开始...

I don't know how I could really ask that any better, hopefully you can both understand this and have a suggestion. 我不知道我如何才能真正提出更好的要求,希望您既可以理解这一点,也可以提出建议。

Thanks for any help you can give. 谢谢你提供的所有帮助。

Chris 克里斯

EDIT: 编辑:

public class EvalVBCode{

        private string _Code;
        private CompilerErrorCollection m_oCompilerErrors = new CompilerErrorCollection();
        private Dictionary<String, String> _ReferencedAssemblies = new Dictionary<String, String>();
        private List<EvalInputObject<dynamic>> _RuntimeVariables = new List<EvalInputObject<dynamic>>();

        public string Code
        {
            get
            {
                return this._Code;
            }

            set
            {
                this._Code = value;
            }
        }

        public CompilerErrorCollection CompilerErrors{ 
            get
            {
                return m_oCompilerErrors;
            }

            set
            {
                m_oCompilerErrors = value;
            }
        }


        public Dictionary<String, String> ReferencedAssemblies
        {
            get
            {
                return this._ReferencedAssemblies;
            }
        }

        public List<EvalInputObject<dynamic>> RuntimeVariables
        {
            get
            {
                return this._RuntimeVariables;
            }
        }

        public delegate void EvalErrorEventHandler(Object sender, EvalErrorEventArgs e);
        public event EvalErrorEventHandler EvalError;

        protected virtual void onEvalError(EvalErrorEventArgs e)
        {
            if (EvalError != null)
                EvalError(this, e);
        }

        public static Object Evaluate(string code)
        {
            EvalVBCode EvalObj = new EvalVBCode();
            EvalObj.Code = code;
            return EvalObj.Evaluate();
        }

        public static Object Evaluate(string code, params EvalInputObject<dynamic>[] parameters)
        {
            EvalVBCode EvalObj = new EvalVBCode();
            EvalObj.Code = code;
            return EvalObj.Evaluate(parameters);
        }

        public static Object Evaluate(string code, EvalErrorEventHandler errorEventHandler)
        {
            EvalVBCode EvalObj = new EvalVBCode();
            EvalObj.Code = code;
            EvalObj.EvalError = errorEventHandler;
            return EvalObj.Evaluate();
        }

        public static Object Evaluate(string code, EvalErrorEventHandler errorEventHandler, params EvalInputObject<dynamic>[] parameters)
        {
            EvalVBCode EvalObj = new EvalVBCode();
            EvalObj.Code = code;
            EvalObj.EvalError = errorEventHandler;
            return EvalObj.Evaluate(parameters);
        }

        public Object Evaluate(params Object[] parameters){

            AppDomainSetup loSetup = new AppDomainSetup();
            loSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            AppDomain loAppDomain = AppDomain.CreateDomain("MyAppDomain", null, loSetup);

             VBCodeProvider oCodeProvider = new VBCodeProvider();
            //Obsolete in 2.0 framework
            //oICCompiler ICodeCompiler = oCodeProvider.CreateCompiler

            CompilerParameters oCParams = new CompilerParameters();
            CompilerResults oCResults; 
            System.Reflection.Assembly oAssy;
            Object oExecInstance = null;
            Object oRetObj  = null;
            MethodInfo oMethodInfo;
            Type oType;

            try{
                //Setup the Compiler Parameters  
                //Add any referencedsemblies
                oCParams.ReferencedAssemblies.Add("system.dll");
                oCParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");
                oCParams.ReferencedAssemblies.Add("Mscorlib.dll");

                //oCParams.ReferencedAssemblies.Add("system.xml.dll");
                //oCParams.ReferencedAssemblies.Add("system.data.dll");

                //Add User Assemblies
                foreach (String Assembly in _ReferencedAssemblies.Keys)
                {
                    if (!oCParams.ReferencedAssemblies.Contains(Assembly))
                        oCParams.ReferencedAssemblies.Add(Assembly);
                }

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

                //Generate the Code Framework
                StringBuilder sb  = new StringBuilder();

                sb.Append("Imports System " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("Imports Microsoft.VisualBasic" + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("Imports System.Math" + Microsoft.VisualBasic.Constants.vbCrLf);

                foreach (String Import in _ReferencedAssemblies.Values.Distinct(StringComparer.InvariantCultureIgnoreCase))
                {
                    if (!Import.IsIn(StringComparer.InvariantCultureIgnoreCase, "SYSTEM", "MICROSOFT.VISUALBASIC", "SYSTEM.MATH"))
                        sb.AppendFormat("Imports {0}" + Microsoft.VisualBasic.Constants.vbCrLf, Import);
                }
                //sb.Append("Imports System.Xml" + Microsoft.VisualBasic.Constants.vbCrLf);
                //sb.Append("Imports System.Data" + Microsoft.VisualBasic.Constants.vbCrLf);

                //Build a little wrapper code, with our passed in code in the middle 
                sb.Append("Namespace EvalVBCode " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("Class EvalVBCodeClass " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("Public Function Eval(");

                if (RuntimeVariables.Count > 0)
                {
                    List<String> MethodParams = new List<String>();
                    foreach (EvalInputObject<dynamic> InputObject in _RuntimeVariables.Distinct())
                    {
                        MethodParams.Add(String.Format("{0} {1}", InputObject.Type.ToString(), InputObject.Name));
                    }
                    sb.Append(String.Join(", ", MethodParams));
                }
                sb.Append(") As Object " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append(this._Code + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("End Function " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("End Class " + Microsoft.VisualBasic.Constants.vbCrLf);
                sb.Append("End Namespace " + Microsoft.VisualBasic.Constants.vbCrLf);
                //Response.Write("<br />" + sb.ToString() + "<br />");

                try{
                    //Compile and get results 
                    //2.0 Framework - Method called from Code Provider
                    oCResults = oCodeProvider.CompileAssemblyFromSource(oCParams, sb.ToString());
                    //1.1 Framework - Method called from CodeCompiler Interface
                    //cr = oICCompiler.CompileAssemblyFromSource (cp, sb.ToString)

                    //Check for compile time errors 
                    if(oCResults.Errors.Count > 0){
                        onEvalError(new EvalErrorEventArgs(this._Code, this._ReferencedAssemblies, this._RuntimeVariables, new Exception(oCResults.Errors[0].ErrorText)));
                        return null;
                    }else{
                        //No Errors On Compile, so continue to process...
                        oAssy = oCResults.CompiledAssembly;
                        oExecInstance = oAssy.CreateInstance("EvalVBCode.EvalVBCodeClass");

                        oType = oExecInstance.GetType();
                        oMethodInfo = oType.GetMethod("Eval");
                        if (parameters.Length > 0)
                        {
                            oRetObj = oMethodInfo.Invoke(oExecInstance, parameters);
                        }
                        else if(RuntimeVariables.Count > 0)
                        {
                            List<Object> RuntimeVariableValues = new List<Object>();
                            foreach(EvalInputObject<dynamic> Variable in _RuntimeVariables)
                            {
                                RuntimeVariableValues.Add(Variable.Value);
                            }
                            oRetObj = oMethodInfo.Invoke(oExecInstance, RuntimeVariableValues.ToArray<Object>());
                        }
                        else
                            oRetObj = oMethodInfo.Invoke(oExecInstance, null);

                        /*if (oRetObj != null)
                            Response.Write("<br />" + oRetObj.GetType().ToString() + " - " + Convert.ToString(oRetObj) + "<br />");*/
                        return oRetObj;
                    }
                }catch(Exception ex){
                    //Compile Time Errors Are Caught Here
                    //Some other weird error 
                    onEvalError(new EvalErrorEventArgs(this._Code, this._ReferencedAssemblies, this._RuntimeVariables, ex));
                    return null;
                }
            }catch(Exception ex){
                onEvalError(new EvalErrorEventArgs(this._Code, this._ReferencedAssemblies, this._RuntimeVariables, ex));
                return null;
            }
            return oRetObj;
        }
    }

    public class EvalErrorEventArgs
    {
        private string code;
        private Dictionary<String, String> _ReferencedAssemblies;
        private List<EvalInputObject<dynamic>> _RuntimeVariables;
        private Exception exception;

        public EvalErrorEventArgs(string Code, Dictionary<String, String> ReferencedAssemblies, List<EvalInputObject<dynamic>> RuntimeVariables, Exception Exception)
        {
            this.code = Code;
            this._ReferencedAssemblies = ReferencedAssemblies;
            this._RuntimeVariables = RuntimeVariables;
            this.exception = Exception;
        }

        public string Code
        {
            get
            {
                return this.code;
            }
        }

        public Dictionary<String, String> ReferencedAssemblies
        {
            get
            {
                return this._ReferencedAssemblies;
            }
        }

        public ReadOnlyCollection<EvalInputObject<dynamic>> RuntimeVariables
        {
            get
            {
                return this._RuntimeVariables.AsReadOnly();
            }
        }

        public Exception Exception
        {
            get
            {
                return this.exception;
            }
        }
    }

Dynamics are incredibly powerful with a good practice to pass the current AppDomain into a static factory method when creating the instance at runtime. 在运行时创建实例时,Dynamics的功能非常强大,并且有很好的实践,可以将当前的AppDomain传递给静态工厂方法。 Your code above "Evaluate" creates the new AppDomain (context) and thus you have no direct access. 您在“评估”上方的代码将创建新的AppDomain(上下文),因此您无法直接访问。 I would insert a debug breakpoint within the Evaluate method to see if you have access to the System.Reflection.Assembly.GetEntryAssembly().FullName. 我将在Evaluate方法内插入调试断点,以查看是否有权访问System.Reflection.Assembly.GetEntryAssembly()。FullName。 If you did then you would still be running under the same domain and could set it as System.Threading.Thread.GetDomain instead of a "new" instance of AppDomain 如果这样做,您仍将在同一域下运行,并将其设置为System.Threading.Thread.GetDomain而不是AppDomain的“新”实例

As far as getting a reference to the instance of the method which instantiated your dynamic assembly: 至于获得对实例化动态程序集的方法实例的引用,请执行以下操作:

1) Easiest to pass a reference to the ctor of the dynamic 2) Pass a reference to public member properties 3) Use a singleton pattern or static construct to "call" which returns the reference you are looking for. 1)最容易将引用传递给动态ctor 2)将引用传递给公共成员属性3)使用单例模式或静态构造进行“调用”,这将返回您要查找的引用。

All of these concepts are fairly complex when dealing with dynamics. 在处理动力学时,所有这些概念都相当复杂。 But if you can find a way to do number 1 above by reading thru some docs you will be in fine shape 但是,如果您可以通过阅读一些文档找到一种做上面的数字1的方法,那么您的状况将会很好

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM