aspnet核心中具有属性类继承关系的复杂自定义模型绑定

问题描述 投票:0回答:1

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

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

IModelBinderBindModelAsync方法被我的Product类调用,我猜这是我应该寻找Data属性并检查其Kind的地方。然后从参数bindingContext.Model中挤出设备数据,并用DataTeddybear替换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-mvc asp.net-core
1个回答
1
投票

我认为文档中提供的解决方案因您使用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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.