如何使自定义模型绑定器自动应用于特定类型的所有属性



编辑:所以事实证明,我的ModelBinder实际上不是问题所在。问题出在我的ValueConverter上。把它留在这里,因为我认为它说明了如何做一个自定义模型绑定器。


这个问题中有很多代码。我试图尽可能彻底和精确。

我有一个Measurement课。我的许多模型都有一个或多个类型Measurement的属性。此类使用ValueConverter作为字符串映射到数据库。我想做的是在表单提交时将表单数据(即字符串(转换为此类型。因此,我创建了一个自定义ModelBinder

public class MeasurementModelBinder : IModelBinder
{
public MeasurementModelBinder() { }
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if(bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if(valueProviderResult == ValueProviderResult.None) { throw new Exception(); }
string valueString = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(valueString)) { throw new Exception(); }
Measurement result = new Measurement(valueString);
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}

我希望此MeasurementModelBinder应用于每个模型中这种类型的所有属性。所以我正在尝试这样做。下面是具有Measurement类型属性的模型示例。

public class Material
{
[Key]
public int Key { get; set; }
public string Name { get; set; }
public Measurement Density { get; set; }
public Measurement Conductivity { get; set; }
[Display(Name="Specific Heat")]
public Measurement SpecificHeat { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public Material() { }
public Material(string name, Measurement density, Measurement conductivity, Measurement specificHeat, string description = "")
{
Name = name;
Density = density;
Description = description;
Conductivity = conductivity;
SpecificHeat = specificHeat;
}
public override string ToString() { return Name; }
}

至于控制器,我现在只是使用测试控制器,这是Visual Studio自动生成的带有视图的MVC实体框架控制器之一的修改版本。下面是控制器中的操作之一。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Material material)
{
material.Created = DateTime.Now;
material.Updated = DateTime.Now;
if (ModelState.IsValid)
{
_context.Add(material);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(material);
}

表单未提供CreatedUpdated字段,因此我自己填写。但是,其他所有内容都由表单填充,作为字符串。您可以在上面的自定义模型绑定器类中看到如何构造Measurement对象。

我创建了一个ModelBinderProvider类,如下所示。

public class MeasurementModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if(context == null) { throw new ArgumentException(nameof(context)); }
if(!context.Metadata.IsComplexType && context.Metadata.ModelType == typeof(Measurement))
{
return new MeasurementModelBinder();
}
return null;
}
}

我已经在Startup.ConfigureServices()方法中注册了提供程序,就像这样。

services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new MeasurementModelBinderProvider());
});

这是正在提交的表单。

<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Density" class="control-label"></label>
<input asp-for="Density" class="form-control" />
<span asp-validation-for="Density" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Conductivity" class="control-label"></label>
<input asp-for="Conductivity" class="form-control" />
<span asp-validation-for="Conductivity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SpecificHeat" class="control-label"></label>
<input asp-for="SpecificHeat" class="form-control" />
<span asp-validation-for="SpecificHeat" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>

我希望提交此表格以向材料表添加一条新记录,并填写所有字段。它不会那样做。相反,它会添加一个新记录,其中所有Measurement属性均为 null。

我尝试将[ModelBinder(BinderType = typeof(MeasurementModelBinder))]属性添加到Measurement类中,但这没有帮助。实际上,它导致了"无法解决服务"错误,当我通过将services.AddScoped<IModelBinder, MeasurementModelBinder>();添加到ConfigureServices()来摆脱该错误时,我仍然从我的操作中得到了相同的结果。

我还尝试将[BindProperty(BinderType typeof(MeasurementModelBinder))]属性添加到模型本身的属性中,但没有帮助。此外,这是我宁愿不做的事情。有没有办法让它适用于所有Measurement类型属性?如果没有,有什么办法让它工作吗?

谢谢。

我尝试了您的代码,当我将[ModelBinder(BinderType = typeof(MeasurementModelBinder))]属性添加到Measurable类时,它对我有用。我认为您应该将[ModelBinder(BinderType = typeof(MeasurementModelBinder))]属性添加到Measurement类中。

相关内容

  • 没有找到相关文章

最新更新