У меня есть служба 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) + " " + 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;
}
}
}