c# - 如何使用一个动态生成的对象作为CodeEffects生成器的数据源

标签 c# asp.net asp.net-mvc reflection system.reflection

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

View 的html是这样的:

@{
    ViewBag.Title = "Post Example";

    Html.CodeEffects().Styles()
        .SetTheme(ThemeType.Gray)
        .Render();
}
@using (Html.BeginForm("Evaluate", "Post", FormMethod.Post))
{
    <div class="area">
        <h1 class="title">Post Example</h1>
        <div style="margin-top:10px;">
            <span>Info:</span>
            <span style="color:Red;">@ViewBag.Message</span>
        </div>
        <div style="margin-top:10px;">
            @{
    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();
            }
        </div>
    </div>
    <div class="area">
        <h1 class="title" style="margin-top:20px;">Rule Test Form</h1>
        @{
    Html.RenderPartial("_PatientForm");
        }
    </div>
}
@{
    Html.CodeEffects().Scripts().Render();
}

Controller 中的索引 Action 是这样的:

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

但是我们想要一个使用反射构建的动态对象,我创建的方法是这样的:

 private static object CreateOurNewObject()
        {
            string _xml = "<root>" +
                "<column name=\"Name\">Miron</column>" +
                "<column name=\"LastName\">Abramson</column>" +
                "<column name=\"Blog\">www.blog.mironabramson.com</column>" +
                "</root>";

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

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

最佳答案

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

将 AssemblybyBuilderAccess.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) 可以找到它。

关于c# - 如何使用一个动态生成的对象作为CodeEffects生成器的数据源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34911152/

相关文章:

c# - 时间预订视觉显示

c# - HashSet<T>.CreateSetComparer 的用法示例

c# - 在不复制属性名称的情况下对 web.config 进行强类型调用?

c# - sql语法错误?在我的 c# img url 字符串中

.net - 使用网站绝对 URL 链接到 CSS/JS 文件

c# - 捆绑链接的 JavaScript 文件

c# - 使用 IModelBinder 自定义模型绑定(bind)

asp.net-mvc - 实体创建抛出 "No parameterless constructor defined for this object"

c# - 如何确定类型是否是 Action/Func 委托(delegate)之一?

c# - 在c#中编码和解码安全吗?