0

我有一个用 C# 编写的 Windows 服务,它根据 XML 文件中的变量发送电子邮件,这包括通过所述 XML 文件中提供的连接字符串访问 SQL 或 MySQL 数据库。我还提供了我称之为“自定义字段”的功能,它评估 C# 或 VB.NET 代码,用户也可以将自定义代码的结果放入 XML 文件中,然后可用于生成电子邮件。现在在自定义代码中,我想提供一组帮助函数,用户可以从他们的代码中调用这些函数,这些函数将检索字段的值,然后他们可以根据需要进行操作。

基本上在 XML 中,用户设置对他们想要访问的每个数据库的引用,然后是该数据库中的每个表,然后是该表中的每个字段,每个都被赋予一个基于 '0'(零)的 ID,每个数据库中的 IE 表id 从(零)开始,并且在每个表中,字段再次从“0”(零)开始

在我所说的面包屑中,对特定字段的引用格式如下:

0,2,6(即数据库 ID 0,数据库 ID 0 内的表 ID 2,数据库 ID 0 内的表 ID 2 内的字段 ID 6)

或者

0,3,6(即数据库 ID 0,数据库 ID 0 内的表 ID 3,数据库 ID 0 内的表 ID 3 内的字段 ID 6)

用户可以在他们的自定义代码中调用帮助函数,这些函数将检索一个或多个值,视情况可能是他们在其 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>

正如您在此自定义代码中看到的,有两个字段被引用,其中一个字段面包屑为“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)

这些面包屑是对 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>
...

本质上,自定义代码将创建一个名为“FullName”的字符串变量,然后检索成员的名字和姓氏,通过“”(空格)字符将它们连接起来,并将结果存储在“FullName”变量中,然后返回结果. 然后可以从消息中调用 customCode,例如“C,0”C=Custom。

现在我的问题来了:

由于数据库字段等的所有处理都是在服务本身内完成的,因此还使用反射从服务内调用自定义代码我如何从帮助程序(MailEngine)类访问服务使用的函数和变量帮助程序(MailEngine)类包含在与服务不同的上下文中的自定义代码中。

例如,如果在 MailEngine 类中我有一个函数如下

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

}

我需要做什么才能从这个函数或这个类中的其他函数中调用函数或检索我在服务中拥有的变量。

这是可能的还是我需要重新解析xml文档重新连接到数据库重新收集所有字段值并从那里开始......

我不知道我如何才能真正提出更好的要求,希望您能理解这一点并提出建议。

谢谢你提供的所有帮助。

克里斯

编辑:

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;
            }
        }
    }
4

1 回答 1

0

动态功能非常强大,在运行时创建实例时,将当前 AppDomain 传递给静态工厂方法是一种很好的做法。您在“评估”上方的代码创建了新的 AppDomain(上下文),因此您无法直接访问。我会在 Evaluate 方法中插入一个调试断点,以查看您是否有权访问 System.Reflection.Assembly.GetEntryAssembly().FullName。如果你这样做了,那么你仍然会在同一个域下运行,并且可以将其设置为 System.Threading.Thread.GetDomain 而不是 AppDomain 的“新”实例

至于获取对实例化动态程序集的方法实例的引用:

1)最容易传递对动态 ctor 的引用 2)传递对公共成员属性的引用 3)使用单例模式或静态构造来“调用”它返回您正在寻找的引用。

在处理动态时,所有这些概念都相当复杂。但是,如果您可以通过阅读一些文档找到执行上述第 1 项的方法,那么您将处于良好状态

于 2011-04-04T00:52:26.237 回答