如何使用动态生成的对象作为CodeEffects生成器的数据源

我们正在使用这个组件www.codeeffects.com,它允许我们根据对象属性创建业务规则。

视图的html是这样的:

@{ ViewBag.Title = "Post Example"; Html.CodeEffects().Styles() .SetTheme(ThemeType.Gray) .Render(); } @using (Html.BeginForm("Evaluate", "Post", FormMethod.Post)) { 

Post Example

Info: @ViewBag.Message
@{ Html.CodeEffects().RuleEditor() .Id("ruleEditor") .SaveAction("Save", "Post") .DeleteAction("Delete", "Post") .LoadAction("Load", "Post") .Mode(RuleType.Execution) .ContextMenuRules(ViewBag.ContextMenuRules) .ToolBarRules(ViewBag.ToolBarRules) .Rule(ViewBag.Rule) .Render(); }

Rule Test Form

@{ Html.RenderPartial("_PatientForm"); }
} @{ Html.CodeEffects().Scripts().Render(); }

控制器中的索引操作如下:

  [HttpGet] public ActionResult Index() { ViewBag.Rule = RuleModel.Create(typeof(Patient)); return View(); } 

Patient患者是这样的:

 // External methods and actions [ExternalMethod(typeof(PatientService), "IsToday")] [ExternalAction(typeof(PatientService), "RequestInfo")] // Dynamic Menu Data Sources; details can be found at // http://codeeffects.com/Doc/Business-Rules-Dynamic-Menu-Data-Sources // The getEducationTypes() client-side function declared in /Views/Shared/_Layout.cshtml [Data("Education", "getEducationTypes")] // The List() method declared by the Physician class [Data("Physicians", typeof(Physician), "List")] public class Patient { // C-tor public Patient() { this.ID = Guid.Empty; this.Gender = Gender.Unknown; } // This property will not appear in the Rule Editor - Code Effects component ignores Guids. // Details at http://codeeffects.com/Doc/Business-Rules-Data-Types public Guid ID { get; set; } [Field(DisplayName = "First Name", Description = "Patient's first name", Max = 30)] public string FirstName { get; set; } [Field(DisplayName = "Last Name", Max = 30, Description = "Patient's last name")] public string LastName { get; set; } [Field(DisplayName = "Email Address", ValueInputType = ValueInputType.User, Max = 150, Description = "Email address of the patient")] public string Email { get; set; } [Field(DisplayName = "Date of Birth", DateTimeFormat = "MMM dd, yyyy")] public DateTime? DOB { get; set; } [Field(ValueInputType = ValueInputType.User, Description = "Patient's gender")] public Gender Gender { get; set; } // This field uses the "Physicians" dynamic menu source (declared at class level) [Field(DisplayName = "Physician", DataSourceName = "Physicians", Description = "Patient's primary physician")] public int PhysicianID { get; set; } // This field uses the "Education" client-side dynamic menu source (declared at class level) [Field(DisplayName = "Education", DataSourceName = "Education", Description = "Patient's education level")] public int EducationTypeID { get; set; } [Field(Min = 0, Max = 200, Description = "Current pulse")] public int? Pulse { get; set; } [Field(Min = 0, Max = 200, DisplayName = "Systolic Pressure", Description = "Current systolic pressure")] public int? SystolicPressure { get; set; } [Field(Min = 0, Max = 200, DisplayName = "Diastolic Pressure", Description = "Current Diastolic pressure")] public int? DiastolicPressure { get; set; } [Field(Min = 0, Max = 110, Description = "Current temperature")] public decimal? Temperature { get; set; } [Field(DisplayName = "Headaches Box", Description = "Does the patient have frequent headaches?")] public bool Headaches { get; set; } [Field(DisplayName = "Allergies Box", Description = "Any allergies?")] public bool Allergies { get; set; } [Field(DisplayName = "Tobacco Box", Description = "Does the patient smoke?")] public bool Tobacco { get; set; } [Field(DisplayName = "Alcohol Box", Description = "Alcohol use?")] public bool Alcohol { get; set; } public Address Home { get; set; } public Address Work { get; set; } // This property is used to display outputs of rule actions [ExcludeFromEvaluation] public string Output { get; set; } [Method("Full Name", "Joins together patient's first and last names")] public string FullName() { return string.Format("{0} {1}", this.FirstName, this.LastName); } // Empty overload of the Register method. // No Method attribute is needed here because its // display name is the same as its declared name. [Action(Description = "Registers new patient")] public void Register() { this.Output = "The patient has been registered"; } // Overload of the Register method that takes one param. // Both overloads can be used in Code Effects as two different actions // as long as their display names are different. [Action("Register with a Message", "Registers new patient with additional info")] public void Register([Parameter(ValueInputType.User, Description = "Output message")] string message) { this.Output = message; } } 

但是我们想要使用reflection构建的dinamyc对象,我创建的方法是这样的:

  private static object CreateOurNewObject() { string _xml = "" + "Miron" + "Abramson" + "www.blog.mironabramson.com" + ""; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(_xml); // create a dynamic assembly and module AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "tmpAssembly"; System.Reflection.Emit.AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule"); // create a new type builder TypeBuilder typeBuilder = module.DefineType("BindableRowCellCollection", TypeAttributes.Public | TypeAttributes.Class); // Loop over the attributes that will be used as the properties names in out new type foreach (XmlNode node in xmlDoc.SelectSingleNode("root").ChildNodes) { string propertyName = node.Attributes["name"].Value; // Generate a private field FieldBuilder field = typeBuilder.DefineField("_" + propertyName, typeof(string), FieldAttributes.Private); // Generate a public property PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, typeof(string), new Type[] { typeof(string) }); // The property set and property get methods require a special set of attributes: MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig; // Define the "get" accessor method for current private field. MethodBuilder currGetPropMthdBldr = typeBuilder.DefineMethod("get_value", GetSetAttr, typeof(string), Type.EmptyTypes); // Intermediate Language stuff... ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator(); currGetIL.Emit(OpCodes.Ldarg_0); currGetIL.Emit(OpCodes.Ldfld, field); currGetIL.Emit(OpCodes.Ret); // Define the "set" accessor method for current private field. MethodBuilder currSetPropMthdBldr = typeBuilder.DefineMethod("set_value", GetSetAttr, null, new Type[] { typeof(string) }); // Again some Intermediate Language stuff... ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator(); currSetIL.Emit(OpCodes.Ldarg_0); currSetIL.Emit(OpCodes.Ldarg_1); currSetIL.Emit(OpCodes.Stfld, field); currSetIL.Emit(OpCodes.Ret); // Last, we must map the two methods created above to our PropertyBuilder to // their corresponding behaviors, "get" and "set" respectively. property.SetGetMethod(currGetPropMthdBldr); property.SetSetMethod(currSetPropMthdBldr); } // Generate our type Type generetedType = typeBuilder.CreateType(); // Now we have our type. Let's create an instance from it: object generetedObject = Activator.CreateInstance(generetedType); // Loop over all the generated properties, and assign the values from our XML: PropertyInfo[] properties = generetedType.GetProperties(); int propertiesCounter = 0; // Loop over the values that we will assign to the properties foreach (XmlNode node in xmlDoc.SelectSingleNode("root").ChildNodes) { string value = node.InnerText; properties[propertiesCounter].SetValue(generetedObject, value, null); propertiesCounter++; } //Yoopy ! Return our new genereted object. return generetedObject; } 

我们如何替换索引操作中的行以使用该对象而不是Patient? typeof(对象),不起作用。

从处理类型创建的CreateOurNewObject()方法中提取一部分。 称之为CreateType (字符串xml)。

将AssemlbyBuilderAccess.Run更改为AssemblyBuilderAccess.RunAndSave。 然后,一旦创建了类型,就调用assemblyBuilder.Save()方法。 将它保存在Assembly.Load将找到的位置(例如bin或.net临时文件夹之一),或者在搜索路径中的任何其他位置。

使用它来创建类型并实例化对象。

然后,在索引中调用

 Type myType = CreateType(xml); RuleModel.Create(myType); 

如果您在外面进行评估,请确保使用相同的类型(不要每次都重新生成)。 这意味着您需要先加载它。

 Type myType = Assembly.Load(assemblyName); object myObject = Activator.CreateInstance(myType); //...populate myObject with necessary values based on your xml Evaluator ev = new Evaluator(myType, rule); bool result = ev.Evaluate(myObject); 

或者您可以使用DynamicEvaluator,它只调用myObject.GetType()

 DynamicEvaluator ev = new DynamicEvaluator(rule); bool result = ev.Evaluate(myObject); 

这应该工作。 这里重要的部分是你首先保存你的程序集(目前无法从内存中读取),并且它位于搜索路径的一部分文件夹中,以便Assembly.Load(name)可以找到它。

使用TypeBuilder ,您可以尝试使用生成类型的现有或自定义界面,然后将其用作类型。

 typeBuilder.AddInterfaceImplementation(typeof(IMyInterface)); 

现在,您创建的对象(在上面的示例中)具有类型IMyInterface ,然后您可以在Index操作中使用它:

 ViewBag.Rule = RuleModel.Create(typeof(IMyInterface));