Доступ к вызову контекста из сборки Reflection в C # - PullRequest
0 голосов
/ 03 апреля 2011

У меня есть служба Windows, написанная на C #, которая отправляет электронные письма на основе переменных в XML-файле, включая доступ к базам данных SQL или MySQL через строки подключения, указанные в указанном XML-файле. Я также даю возможность для того, что я называю «Пользовательские поля», которые оценивают код C # или VB.NET, который пользователь также может поместить в файл XML, а затем результат пользовательского кода может быть использован для создания электронных писем. Теперь в пользовательском коде я хочу предоставить набор вспомогательных функций, которые пользователь может вызывать из своего кода, который будет извлекать значение для поля, которым они могут затем манипулировать по своему усмотрению.

По сути, в XML пользователь устанавливает ссылки на каждую базу данных, к которой он хочет получить доступ, затем к каждой таблице в этой базе данных, а затем к каждому полю в этой таблице, каждому из которых присваивается идентификатор, основанный на «0» (ноль) I.E. в каждой базе данных идентификатор таблицы начинается с (нуля), а внутри каждой таблицы снова поля начинаются с '0' (нуля)

Ссылка на определенное поле отформатирована следующим образом в том, что я называю хлебными крошками:

0,2,6 (Который является идентификатором базы данных 0, идентификатором таблицы 2 в идентификаторе базы данных 0, идентификатором поля 6 в идентификаторе таблицы 2 в идентификаторе базы данных 0)

или

0,3,6 (то есть идентификатор базы данных 0, идентификатор таблицы 3 в базе данных 0, идентификатор поля 6 в таблице 3, идентификатор базы данных 0)

Пользователь может вызывать вспомогательные функции в своем пользовательском коде, который будет извлекать значение или значения, в зависимости от обстоятельств любого поля, на которое они ссылались в своем 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" (идентификатор базы данных 0, идентификатор таблицы 0 в базе данных 0, идентификатор поля 1 в таблице 0 в пределах База данных с идентификатором 0) и две с полем хлебных крошек "0,0,2" (База данных с идентификатором 0, Таблица с идентификатором 0 в базе данных с идентификатором 0, Поле с идентификатором 2 в таблице с идентификатором 0 и идентификатором базы данных 0)

Эти хлебные крошки являются ссылками на схему, представленную в 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.

Теперь вот мой вопрос:

Поскольку вся обработка полей базы данных и т. Д. Выполняется внутри самой службы, пользовательский код вызывается также изнутри службы, используя Reflections, как из класса помощника (MailEngine) получить доступ к функциям и переменным, используемым служба видит, что вспомогательный класс (MailEngine) включен в пользовательский код, который находится в другом контексте службы.

Например, если в классе MailEngine у ​​меня есть следующая функция

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

}

Что мне нужно сделать, чтобы изнутри этой функции или других в этом классе, чтобы я мог вызывать функции или получать переменные, которые у меня есть в службе.

Возможно ли это, или мне нужно повторно проанализировать документ xml, заново подключиться к базе данных, вспомнить все значения полей и перейти оттуда ...

Я не знаю, как я мог бы спросить об этом лучше, надеюсь, вы оба поймете это и получите предложение.

Спасибо за любую помощь, которую вы можете оказать.

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

1 Ответ

0 голосов
/ 04 апреля 2011

Динамика невероятно мощная, с хорошей практикой передачи текущего AppDomain в статический метод фабрики при создании экземпляра во время выполнения. Ваш код выше «Evaluate» создает новый домен приложений (контекст), и поэтому у вас нет прямого доступа. Я бы вставил отладочную точку останова в метод Evaluate, чтобы проверить, есть ли у вас доступ к System.Reflection.Assembly.GetEntryAssembly (). FullName. Если вы это сделаете, то вы все равно будете работать в том же домене и сможете установить его как System.Threading.Thread.GetDomain вместо «нового» экземпляра AppDomain

Что касается получения ссылки на экземпляр метода, который создал вашу динамическую сборку:

1) Проще всего передать ссылку на динамический динамик 2) Передать ссылку на общедоступные свойства участника 3) Используйте одноэлементный шаблон или статическую конструкцию для вызова, которая возвращает ссылку, которую вы ищете.

Все эти понятия довольно сложны при работе с динамикой. Но если вы сможете найти способ сделать номер 1 выше, прочитав несколько документов, вы будете в хорошей форме

...