.NET Core MVC组合模式规则引擎UI编辑器问题求助
解决思路:.NET Core组合模式下规则引擎UI编辑器的模型绑定与树形处理
一、自定义ModelBinder属性绑定失败的修复
补全属性赋值逻辑
你用Activator.CreateInstance创建实例后,需要通过反射遍历实例的公共属性,从ValueProvider中提取对应键值并赋值。如果是组合模式中的集合属性(如Children),还要递归处理子节点的绑定:public async Task BindModelAsync(ModelBindingContext bindingContext) { var ruleTypeStr = bindingContext.ValueProvider.GetValue("RuleType").FirstValue; var ruleType = Type.GetType(ruleTypeStr); if (ruleType == null) { bindingContext.Result = ModelBindingResult.Failed(); return; } var ruleInstance = Activator.CreateInstance(ruleType); // 绑定基础属性 foreach (var prop in ruleType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var valueResult = bindingContext.ValueProvider.GetValue(prop.Name); if (valueResult != ValueProviderResult.None) { try { var propValue = Convert.ChangeType(valueResult.FirstValue, prop.PropertyType); prop.SetValue(ruleInstance, propValue); } catch { /* 处理类型转换异常 */ } } } // 递归绑定子节点集合(以Children为例) var childrenProp = ruleType.GetProperty("Children"); if (childrenProp != null && typeof(IList<RuleBase>).IsAssignableFrom(childrenProp.PropertyType)) { var childrenList = (IList<RuleBase>)Activator.CreateInstance(childrenProp.PropertyType); int index = 0; while (true) { var childTypeValue = bindingContext.ValueProvider.GetValue($"Children[{index}].RuleType").FirstValue; if (string.IsNullOrEmpty(childTypeValue)) break; // 递归调用绑定子节点 var childBindingContext = DefaultModelBindingContext.CreateBindingContext( bindingContext.ActionContext, bindingContext.ValueProvider, bindingContext.MetadataProvider, new ModelMetadataIdentity(typeof(RuleBase)), $"Children[{index}]" ); await bindingContext.BinderModelBinder.BindModelAsync(childBindingContext); if (childBindingContext.Result.IsModelSet) { childrenList.Add((RuleBase)childBindingContext.Result.Model); } index++; } childrenProp.SetValue(ruleInstance, childrenList); } bindingContext.Result = ModelBindingResult.Success(ruleInstance); }确保请求体键名符合层级约定
提交的表单/JSON键名必须和模型属性层级对应,比如子节点的键名要写成Children[0].Id、Children[0].RuleType,这样ModelBinder才能正确识别层级关系。正确注册ModelBinder
要么在全局注册BinderProvider,要么给基类添加特性:// 全局注册 services.AddControllersWithViews(options => { options.ModelBinderProviders.Insert(0, new RuleModelBinderProvider()); }); // 或者给RuleBase类添加特性 [ModelBinder(BinderType = typeof(RuleModelBinder))] public abstract class RuleBase { /* ... */ }
二、树形结构渲染与表单绑定
- 递归渲染树形节点:使用局部视图递归渲染每个规则节点,每个节点的表单元素
name属性严格遵循MVC集合绑定规则。例如:@model RuleBase <div class="rule-node"> <input type="hidden" name="@ViewData.TemplateInfo.GetFullHtmlFieldName("RuleType")" value="@Model.GetType().AssemblyQualifiedName" /> <input type="text" name="@ViewData.TemplateInfo.GetFullHtmlFieldName("Name")" value="@Model.Name" /> @if (Model is CompositeRule composite) { <div class="children"> @for (int i = 0; i < composite.Children.Count; i++) { @Html.EditorFor(m => composite.Children[i], "RuleNode", new { ViewData = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = $"{ViewData.TemplateInfo.HtmlFieldPrefix}.Children[{i}]" } } }) } </div> } </div> - 添加RuleType隐藏字段:每个节点必须携带自身类型的完整名称,让ModelBinder能创建正确的实例类型。
三、XML序列化与数据库存储
配置XML序列化子类识别:给规则基类添加
XmlInclude特性,确保序列化/反序列化时能识别子类:[XmlInclude(typeof(CompositeRule))] [XmlInclude(typeof(LeafRule))] public abstract class RuleBase { public int Id { get; set; } public string Name { get; set; } } public class CompositeRule : RuleBase { public List<RuleBase> Children { get; set; } = new(); } public class LeafRule : RuleBase { public string Condition { get; set; } }序列化与保存逻辑:
public string SerializeRuleToXml(RuleBase rule) { var serializer = new XmlSerializer(typeof(RuleBase)); using var writer = new StringWriter(); serializer.Serialize(writer, rule); return writer.ToString(); } // 保存到数据库:将XML字符串存入对应字段 // 读取时反序列化: public RuleBase DeserializeXmlToRule(string xml) { var serializer = new XmlSerializer(typeof(RuleBase)); using var reader = new StringReader(xml); return (RuleBase)serializer.Deserialize(reader); }
内容的提问来源于stack exchange,提问作者Zamash
相关产品推荐
相关产品推荐

