我最初将此问题发布到 GitHub:https://github.com/aspnet/Mvc/issues/8723
这里有一个 GitHub 存储库,其中重现了该问题: https: //github.com/Costo/aspnetcore-binding-bug
我正在使用 ASP.NET Core 2.2 Preview 3。
当在“子”模型数组的属性上使用自定义模型绑定器(带有 [ModelBinder] 属性)时,请求的模型绑定阶段会进入无限循环。看这个截图:
如果在顶级模型属性上使用,自定义模型绑定器效果很好,但我想了解为什么它在子模型数组中使用时不起作用。任何对此的帮助将不胜感激。
谢谢 !
这是模型、控制器、视图和自定义绑定器的代码:
该模型:
public class TestModel
{
public TestInnerModel[] InnerModels { get; set; } = new TestInnerModel[0];
[ModelBinder(BinderType = typeof(NumberModelBinder))]
public decimal TopLevelRate { get; set; }
}
public class TestInnerModel
{
public TestInnerModel()
{
}
[ModelBinder(BinderType = typeof(NumberModelBinder))]
public decimal Rate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
自定义模型绑定器(故意简化以不执行任何特殊操作):
public class NumberModelBinder : IModelBinder
{
private readonly NumberStyles _supportedStyles = NumberStyles.Float | NumberStyles.AllowThousands;
private DecimalModelBinder _innerBinder;
public NumberModelBinder(ILoggerFactory loggerFactory)
{
_innerBinder = new DecimalModelBinder(_supportedStyles, loggerFactory);
}
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)
{
return _innerBinder.BindModelAsync(bindingContext);
}
}
Run Code Online (Sandbox Code Playgroud)
控制器:
public class HomeController : Controller
{
public IActionResult Index()
{
return View(new TestModel
{
TopLevelRate = 20m,
InnerModels = new TestInnerModel[]
{
new TestInnerModel { Rate = 2.0m },
new TestInnerModel { Rate = 0.2m }
}
});
}
[HttpPost]
public IActionResult Index(TestModel model)
{
return Ok();
}
}
Run Code Online (Sandbox Code Playgroud)
剃刀视图:
@model TestModel;
<form asp-controller="Home" asp-action="Index" method="post" role="form">
<div>
<input asp-for="@Model.TopLevelRate" type="number" min="0" step=".01" />
</div>
<div>
@for (var i = 0; i < Model.InnerModels.Length; i++)
{
<input asp-for="@Model.InnerModels[i].Rate" type="number" min="0" step=".01" />
}
</div>
<input type="submit" value="Go" />
</form>
Run Code Online (Sandbox Code Playgroud)
GitHub问题上发布了解决方案:
@Costo问题是您没有通知模型绑定系统绑定器使用值提供程序。始终
ComplexTypeModelBinder相信数据可用于下一个TestInnerModel实例,并且最外层的绑定器 (CollectionModelBinder) 会永远持续下去。为了解决这个问题,
[MyModelBinder]
public decimal Rate { get; set; }
private class MyModelBinderAttribute : ModelBinderAttribute
{
public MyModelBinderAttribute()
: base(typeof(NumberModelBinder))
{
BindingSource = BindingSource.Form;
}
}
Run Code Online (Sandbox Code Playgroud)
换句话说,在这种情况下
BindingSource.Custom默认[ModelBinder]使用是不正确的。幸运的是,容器中 POCO 类型属性的自定义模型绑定器应该是极少数情况之一。