You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中如何通过ModelMetaDataIdentity获取ModelMetaData?

多态递归模型绑定器的嵌套绑定问题

类结构

abstract class Parent 
{

}

class X : Parent
{
 Parent ParProp;
}

class Y : Parent
{
 List<Parent> ListParProp;
}

class Z : Parent
{
 //Other properties.
}

绑定器现状

我为Parent类实现了自定义多态递归模型绑定器及对应提供程序。当前逻辑是:通过隐藏输入标识元素类型,用MetadataProvider.GetMetadataForType按类型绑定元素,再特殊绑定Parent类型的子元素(或嵌套IEnumerable中最终类型为Parent的元素),最后通过反射将绑定的子元素附加到父元素。

近期发现绑定器会过多循环调用Parent模型绑定器,原因是绑定子元素时未关联容器实例,绑定完成后无法自动将实例设置到容器中。

测试发现属性的绑定方式由ModelMetaData决定:

  • ModelMetaDataProvider接口的GetMetadataForProperty方法因属性类型为Parent会触发无限递归;
  • ModelMetaDataIdentity的静态方法ForProperty(PropertyInfo propertyInfo, Type modelType, Type containerType)能创建包含正确ModelType和容器信息的标识,理论上可识别Parent子类实现作为容器类成员属性的情况,但ModelMetaData是抽象类,其唯一构造函数ModelMetaData(ModelMetaDataIdentity identity)为受保护,无法直接调用获取实例。

问题影响

系统虽能运行,但必须将大量变量设为可空类型——因为内置复杂对象绑定后,Parent类型属性为空,自动验证器会报错。

现有方案考量

  • 反射调用受保护构造函数:极易因框架更新失效,不愿采用;
  • 保留原有粗糙实现:将空值视为软件局限性,非理想方案。

希望找到合规方法,获取Parent子类实现的ModelMetaData并保留其属性关联信息,让自动绑定器承担更多工作。

补充代码(基于微软多态绑定器改造,处理嵌套多态绑定)

BinderProvider

public IModelBinder GetBinder(ModelBinderProviderContext context)
{
    if (context.Metadata.ModelType != typeof(ParentModel))
    {
        return null;
    }

    var subclasses = new[] {typeof(X), typeof(Y), typeof(Z) }; //实际代码中包含所有Parent的实现类

    var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
    foreach (var type in subclasses)
    {
        var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
        binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
    }

    {
        Type type = typeof(Parent);
        var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
        binders[type] = (modelMetadata, null);
    }

    return new ParentModelBinder(binders);
}

Binder

public async Task BindModelAsync(ModelBindingContext bindingContext)
{
    var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Parent));
    var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;

    IModelBinder modelBinder;
    ModelMetadata modelMetadata;

    Type? modelType = Type.GetType(modelsNamespace + modelTypeValue);

    if (modelType != null && binders.ContainsKey(modelType))
    {
        (modelMetadata, modelBinder) = binders[modelType];
    }
    else
    {
        bindingContext.Result = ModelBindingResult.Failed();
        return;
    }

    var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
    bindingContext.ActionContext,
    bindingContext.ValueProvider,
    modelMetadata,
    bindingInfo: null,
    bindingContext.ModelName);

    await modelBinder.BindModelAsync(newBindingContext);

    if (newBindingContext.Result.IsModelSet)
    {
        // 递归分配属性
        var qemProperties = ReflectorHelper.GetParentMembersFromType(modelType);

        foreach (var property in qemProperties)
        {
            await BindProperty(property, newBindingContext);
        }

        // 设置ValidationState确保派生类型属性正确
        bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
        {
            Metadata = modelMetadata,
        };
    }

    bindingContext.Result = newBindingContext.Result;
}

当前绑定器的问题行为

  • 提供程序获取元数据/绑定器组合列表,用于获取对应元数据和绑定器;
  • 按类型绑定Parent类型的模型;
  • 当Parent实现类包含Parent类型属性(或最终类型为Parent的IEnumerable)时,会循环绑定这些属性并附加到父类型,原因是绑定Parent实现类时未包含容器信息。

现寻求解决方案,如何实现需求?


内容的提问来源于stack exchange,提问作者Nur Luski Levi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:14:55