我正在尝试使用继承的子类在 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;
}
}