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

.NET Core 多层嵌套继承对象的自定义模型绑定实现问题

是否可以为嵌套继承对象创建自定义Custom Model Binder?

我已查阅官方的自定义模型绑定文档,但我的场景更为复杂,涉及的对象结构复杂度很高。

以下是我使用的简化结构示例:

第一层

public class Top
{
    public string Value { set; get; }
    public IEnumerable<MiddleBase> MiddleClasses { set; get; }
}

第二层

public abstract class MiddleBase
{
    public ConcreteMiddleType Type { set; get; }
    public IEnumerable<BottomBase> BottomClasses { set; get; }
}

public class ConcreteMiddle1 : MiddleBase
{
    public string Value1 { set; get; }
}

public class ConcreteMiddle2 : MiddleBase
{
    public string Value2 { set; get; }
}

第三层

public abstract class BottomBase
{
    public ConcreteBottomType Type { set; get; }
}

public class ConcreteBottom1 : BottomBase
{
    public string Value1 { set; get; }
}

public class ConcreteBottom2 : BottomBase
{
    public string Value2 { set; get; }
}

我在处理请求时出现报错,无法完成抽象基类的绑定。请问是否有开发者遇到过同类问题,有相关处理经验可以分享?


解决方案

可以实现,你需要为每层抽象基类分别编写自定义模型绑定器,通过Type枚举字段判断要实例化的具体子类,再复用默认绑定逻辑完成子类属性的绑定,同时支持递归处理嵌套的抽象类集合。

步骤1:实现MiddleBase自定义绑定器

public class MiddleBaseModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // 读取当前对象的Type枚举值
        var typeValueResult = bindingContext.ValueProvider.GetValue($"{bindingContext.ModelName}.{nameof(MiddleBase.Type)}");
        if (typeValueResult == ValueProviderResult.None || !Enum.TryParse<ConcreteMiddleType>(typeValueResult.FirstValue, out var middleType))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // 根据Type实例化对应的具体子类
        MiddleBase instance = middleType switch
        {
            ConcreteMiddleType.Middle1 => new ConcreteMiddle1(),
            ConcreteMiddleType.Middle2 => new ConcreteMiddle2(),
            _ => null
        };

        if (instance == null)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // 绑定实例的所有属性,包括嵌套的BottomBase集合(会自动触发BottomBase的绑定器)
        bindingContext.Model = instance;
        var propertyBindingInfo = bindingContext.ValidationState[instance];
        propertyBindingInfo.SuppressValidation = false;

        foreach (var property in bindingContext.ModelMetadata.Properties)
        {
            var propertyBindingContext = DefaultModelBindingContext.CreateBindingContext(
                bindingContext.ActionContext,
                bindingContext.ValueProvider,
                property,
                bindingInfo: null,
                modelName: $"{bindingContext.ModelName}.{property.PropertyName}");

            await bindingContext.ModelBinderFactory.CreateBinder(propertyBindingContext).BindModelAsync(propertyBindingContext);

            if (propertyBindingContext.Result.IsModelSet)
            {
                property.PropertySetter.SetValue(bindingContext.Model, propertyBindingContext.Result.Model);
            }
        }

        bindingContext.Result = ModelBindingResult.Success(instance);
    }
}

步骤2:实现BottomBase自定义绑定器

逻辑和MiddleBase绑定器完全一致,只需要替换对应的枚举和基类即可:

public class BottomBaseModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        var typeValueResult = bindingContext.ValueProvider.GetValue($"{bindingContext.ModelName}.{nameof(BottomBase.Type)}");
        if (typeValueResult == ValueProviderResult.None || !Enum.TryParse<ConcreteBottomType>(typeValueResult.FirstValue, out var bottomType))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        BottomBase instance = bottomType switch
        {
            ConcreteBottomType.Bottom1 => new ConcreteBottom1(),
            ConcreteBottomType.Bottom2 => new ConcreteBottom2(),
            _ => null
        };

        if (instance == null)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        bindingContext.Model = instance;
        foreach (var property in bindingContext.ModelMetadata.Properties)
        {
            var propertyBindingContext = DefaultModelBindingContext.CreateBindingContext(
                bindingContext.ActionContext,
                bindingContext.ValueProvider,
                property,
                bindingInfo: null,
                modelName: $"{bindingContext.ModelName}.{property.PropertyName}");

            await bindingContext.ModelBinderFactory.CreateBinder(propertyBindingContext).BindModelAsync(propertyBindingContext);

            if (propertyBindingContext.Result.IsModelSet)
            {
                property.PropertySetter.SetValue(bindingContext.Model, propertyBindingContext.Result.Model);
            }
        }

        bindingContext.Result = ModelBindingResult.Success(instance);
    }
}

步骤3:实现绑定器提供者

避免手动给每个属性加[ModelBinder]特性,自动匹配抽象基类对应的绑定器:

public class CustomModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType == typeof(MiddleBase))
        {
            return new BinderTypeModelBinder(typeof(MiddleBaseModelBinder));
        }
        if (context.Metadata.ModelType == typeof(BottomBase))
        {
            return new BinderTypeModelBinder(typeof(BottomBaseModelBinder));
        }
        return null;
    }
}

步骤4:注册绑定器

在Program.cs中添加配置:

builder.Services.AddControllers(options =>
{
    options.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
});

注意事项

  • 如果你是用JSON格式提交请求,也可以直接用System.Text.Json的JsonConverter实现多态序列化/反序列化,实现起来比模型绑定器更简洁,只需要为MiddleBase和BottomBase分别编写JsonConverter即可。
  • 绑定逻辑中复用了默认的属性绑定逻辑,不需要手动解析每个字段,支持默认绑定的所有特性,包括数据注解验证。

内容的提问来源于stack exchange,提问作者Konstantin Shutko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 09:15:03