首页 > 解决方案 > aspnet 核心中的复杂自定义模型绑定与属性类中的继承

问题描述

我正在尝试使用继承的子类在 Core(2.2/3.1) 中进行自定义模型绑定。

我使用IModelBinderProviderIModelBinder来操作我的模型绑定,因为 MVC 不知道是将基类转换Device为 aTeddybear还是 a Legobrick

IModelBinderBindModelAsync方法被我的Product类调用,我想那是我应该查找Data属性并检查它的Kind. 然后从参数中bindingContext.Model挤出设备数据并将Data属性的值替换为Teddybearor Legobrick
但是bindingContext.Model为空;我没有数据。

在MSDN的底部有一个示例,但其中的根是基类。
我有一个常规的根,但一个属性是一个基/继承的类构造。

在某个地方我没有正确连接呼叫,或者我没有找到读取数据的正确方法。


我想我IModelBinderProvider是正确的,它捕获Product类型并将绑定器添加到子类TeddybearLegobrick.

public class DeviceTypeDataContractProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();

        if (context.Metadata.ModelType == typeof(Product))
        {
            foreach (var type in new[] { typeof(Teddybear), typeof(Legobrick) })
            {
                var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
                binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
            }
        }
        else
        {
            return null;
        }

        return new DeviceModelBinder(binders);
    }
}

IModelBinder/处的代码BindModelAsync仍然让我无法理解。

public class DeviceModelBinder : IModelBinder
{
    private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;

    public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
    {
        this.binders = binders;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext){
        ... I totally lost it here and am beginning to feel dizzy.
    }
}

通过互联网来一个电话,如:

"product": {
    "id": "56-1",
    "data": {
        "kind": "teddy",
        "name": "Tutu"
    }
}

或者

"product": {
    "id": "66-1",
    "data": {
        "kind": "lego",
        "studCount": 8
    }
}

Aspnet 用于填充:

public class Product{
    string Id {get;set;}
    Device Data{get;set;}
}
public class Device{
    string Kind {get;set}
}
public class Teddybear: Device{
    string Name {get;set}
}
public class Legobrick: Device{
    int StudCount {get;set}
}

控制器是常规的,并且连接了自定义建模:

[HttpPost]
public async Task<IActionResult> Create([FromBody] Product product){...
services.AddMvc(options => {
    ...
    options.Filters.Add(new AuthorizeFilter(policy));
})
.AddJsonOptions(options => {
     options.ModelBinderProviders.Insert(0, new DeviceTypeDataContractProvider());
});

标签: c#asp.net-mvcasp.net-core

解决方案


我认为文档中提供的解决方案不适用于您的情况,因为您使用json. 一个简单的工作示例是

public class DeviceTypeDataContractProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType == typeof(Product))
        {
            return new DeviceModelBinder();
        }
        return null;
    }
}

public class DeviceModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var reader = new JsonTextReader(new StreamReader(bindingContext.HttpContext.Request.Body));
        //loading request json
        var jObject = JObject.Load(reader);
        JToken data = jObject["data"];

        Product result = jObject.ToObject<Product>();
        switch (result.Data.Kind)
        {
            case "teddy":
                result.Data = data.ToObject<Teddybear>();
                break;
            case "lego":
                result.Data = data.ToObject<Legobrick>();
                break;
            default:
                bindingContext.Result = ModelBindingResult.Failed();
                return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(result);
        return Task.CompletedTask;
    }
}

推荐阅读