我正在尝试使用继承的子类在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());
});
我认为文档中提供的解决方案因您使用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;
}
}