【问题标题】:complex custom model binding in aspnet core with inheritance in property's classasp net core中的复杂自定义模型绑定与属性类中的继承
【发布时间】:2019-12-13 22:12:59
【问题描述】:

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

我使用IModelBinderProvider 和IModelBinder 来操作我的模型绑定,因为MVC 不知道是将基类Device 转换为Teddybear 还是Legobrick。

IModelBinder 的BindModelAsync 方法被我的Product 类调用,我想这就是我应该寻找Data 属性并检查它的Kind 的地方。然后从参数bindingContext.Model 中提取设备数据并将Data 属性的值替换为Teddybear 或Legobrick。
但是bindingContext.Model 为空;我没有数据。

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

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


我猜我的IModelBinderProvider 是正确的,它捕获了Product 类型并将绑定器添加到子类Teddybear 和Legobrick。

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】:

    我认为文档中提供的解决方案不适用于您的情况,因为您使用了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;
        }
    }
    

    【讨论】:

    • 该死!我确信IModelBinder 是正确的选择。我的计划是让 Aspnet 解析任何传入的格式并在“dotnet 级别”处理数据转换。您或任何人是否知道在哪里介入以获取更多 dotnet-ish 对象来处理?
    • @LosManos 不确定在“dotnet 级别”. As you can see in my answer IModelBinder ` is 是什么意思,因为答案依赖于它。无论如何,如果你想创建一个自定义绑定,你仍然需要使用IModelBinder
    • 我希望收到一个 dotnet Product 对象,其中 Data 设置为 Device 这是我将读取的 Kind 属性,然后将 Data 转换为 Teddybear 或 Legobrick .看来我不得不处理 Json,这意味着我可能必须处理 XML 以及通过网络发送的任何其他内容 - Aspnet 通常会为我处理这些内容。
    猜你喜欢
    • 2021-08-12
    • 2020-11-16
    • 2021-03-13
    • 2019-11-11
    • 2013-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多