自定义模型绑定程序不适用于嵌套属性(ASP.NET Core MVC 2)

时间:2018-06-27 10:55:46

标签: datetime asp.net-core-mvc custom-model-binder

我正在尝试编写自定义日期时间模型活页夹,该活页夹用于将格式为“ dd / MM / yyyy”的发布字符串转换为服务器上的有效日期(服务器日期格式为“ MM / dd / yyyy”)。我的源代码如下:

查看模型

TestViewModel.cs

namespace AcaraDataRequestApplication.Models
{
    public class TestViewModel
    {
        public int Id { get; set; }
        public FirstNestedViewModel FirstNested { get; set; }
    }

    public class FirstNestedViewModel
    {
        public string Name { get; set; }
        public SecondNestedViewModel SecondNested { get; set; }
    }

    public class SecondNestedViewModel
    {
        public string Code { get; set; }

        [Required]
        //[CustomDateTimeModelBinder(DateFormat = "dd/MM/yyyy")]
        [ModelBinder(typeof(CustomDateTimeModelBinder))]
        public DateTime ReleaseDate { get; set; }
    }
}

如您所见,我想使用自定义日期时间模型活页夹(发布日期格式:dd / MM / yyyy和服务器日期格式,基于当前区域性设置,从表单发布数据中绑定ReleaseDate属性的值,我的情况是MM / dd / yyyy。

自定义模型活页夹

CustomDateTimeModelBinder.cs

public class CustomDateTimeModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if(bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        if(bindingContext.ModelType != typeof(DateTime))
        {
            return Task.CompletedTask;
        }

        string modelName = GetModelName(bindingContext);
        ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
        if(valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        string dateToParse = valueProviderResult.FirstValue;
        if(string.IsNullOrEmpty(dateToParse))
        {
            return Task.CompletedTask;
        }

        DateTime dateTimeResult = ParseDate(bindingContext, dateToParse);

        if(dateTimeResult == DateTime.MinValue)
        {
            bindingContext.ModelState.TryAddModelError(modelName, $"The value '{dateToParse}' is not valid.");
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(dateTimeResult);

        return Task.CompletedTask;
    }
}

基本上,自定义模型绑定程序的逻辑是将格式为dd / MM / yyyy的发布字符串转换为服务器上的有效日期(服务器日期格式:MM / dd / yyyy)。这意味着发布的字符串值:“ 27/06/2018”将在服务器上正确转换为2018年6月27日。

CustomDateTimeModelBinderAttribute.cs

namespace AcaraDataRequestApplication.ModelBinders
{
    public class CustomDateTimeModelBinderAttribute: ModelBinderAttribute
    {
        public string DateFormat { get; set; }

        public CustomDateTimeModelBinderAttribute() : 
            base(typeof(CustomDateTimeModelBinder))
        {
        }
    }
}

此属性继承了ModelBinderAttribute,它使我可以直接在属性级别装饰该属性,而不是使用ModelBinder属性。

CustomDateTimeModelBinderProvider.cs

namespace AcaraDataRequestApplication.ModelBinders
{
    public class CustomDateTimeModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if(context.Metadata.ModelType == typeof(DateTime))
            {
                return new CustomDateTimeModelBinder();
            }

            return null;
        }
    }
}

如有必要,此提供程序可以帮助我在应用程序级别注册自定义日期时间模型联编程序

查看

DateOnNestedModel.cshtml

@model TestViewModel

@{
    ViewData["Title"] = "Date On Nested Model";
}

<form asp-controller="Home" asp-action="DateOnNestedModel" method="post">
    <div class="form-group">
        <label>Choose a date</label>
        <div class="input-group date" data-provide="datepicker" data-date-format="dd/mm/yyyy" data-date-autoclose="true" data-date-today-highlight="true">
            <input type="text" class="form-control" asp-for="FirstNested.SecondNested.ReleaseDate">
            <div class="input-group-addon">
                <span class="glyphicon glyphicon-calendar"></span>
            </div>
        </div>
        <span asp-validation-for="FirstNested.SecondNested.ReleaseDate" class="text-danger"></span>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</form>

<div class="row">
    <p>@Model?.FirstNested?.SecondNested?.ReleaseDate.ToString()</p>
</div>

@section Scripts
{
    @Html.Partial("_DatepickerScriptsPartial");
}

在“剃刀视图”中,我只有一个使用引导日期选择器的日期字段,该日期选择器以以下格式显示日期:dd / mm / yyyy。 (请注意,如果Razor视图包含与TestViewModel对应的所有字段,则我的自定义日期时间模型绑定程序可以正常运行。但是,如果Razor视图仅包含一个日期字段,则我的自定义日期时间模型绑定程序不会运行。

控制器

HomeController.cs

namespace AcaraDataRequestApplication.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult DateOnNestedModel()
        {
            return View();
        }

        [HttpPost]
        public IActionResult DateOnNestedModel(TestViewModel viewModel)
        {
            if(ModelState.IsValid)
            {

            }

            return View(viewModel);
        }
    }
}

演示

情况1:自定义日期时间模型绑定程序未运行

在屏幕上,我为日期字段选择“ 27/06/2018”,然后单击“提交”按钮,但是在服务器上,我意识到自定义日期时间模型绑定程序的代码在调试时不会运行。

Screen

viewModel.FirstNested.SecondNested.ReleaseDate not bind when debugging

案例2:当我在应用程序级别注册模型联编程序时,自定义日期时间模型联编程序正常运行

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new CustomDateTimeModelBinderProvider());
        });
    }
}

TestViewModel.cs

public class SecondNestedViewModel
{
    public string Code { get; set; }

    [Required]
    //[CustomDateTimeModelBinder(DateFormat = "dd/MM/yyyy")]
    //[ModelBinder(typeof(CustomDateTimeModelBinder))]
    public DateTime ReleaseDate { get; set; }
}

我做了与案例1类似的步骤,这次,我意识到自定义日期时间模型绑定程序的代码在调试时就运行了。

viewModel.FirstNested.SecondNested.ReleaseDate bound when debugging

情况3:当我绑定名为TestViewModel.FirstNested.SecondNested.Code的其他字段时,自定义日期时间模型绑定程序正常运行

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
        {
            //options.ModelBinderProviders.Insert(0, new CustomDateTimeModelBinderProvider());
        });
    }
}

TestViewModel.cs

public class SecondNestedViewModel
{
    public string Code { get; set; }

    [Required]
    //[CustomDateTimeModelBinder(DateFormat = "dd/MM/yyyy")]
    [ModelBinder(typeof(CustomDateTimeModelBinder))]
    public DateTime ReleaseDate { get; set; }
}

DateOnNestedModel.cshtml

@model TestViewModel

@{
    ViewData["Title"] = "Date On Nested Model";
}

<form asp-controller="Home" asp-action="DateOnNestedModel" method="post">
    <div class="form-group">
        <label asp-for="FirstNested.SecondNested.Code">Code</label>
        <input type="text" class="form-control" asp-for="FirstNested.SecondNested.Code" />
    </div>

    <div class="form-group">
        <label>Choose a date</label>
        <div class="input-group date" data-provide="datepicker" data-date-format="dd/mm/yyyy" data-date-autoclose="true" data-date-today-highlight="true">
            <input type="text" class="form-control" asp-for="FirstNested.SecondNested.ReleaseDate">
            <div class="input-group-addon">
                <span class="glyphicon glyphicon-calendar"></span>
            </div>
        </div>
        <span asp-validation-for="FirstNested.SecondNested.ReleaseDate" class="text-danger"></span>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</form>

<div class="row">
    <p>@Model?.FirstNested?.SecondNested?.ReleaseDate.ToString()</p>
</div>

@section Scripts
{
    @Html.Partial("_DatepickerScriptsPartial");
}

在屏幕上,我在代码字段中输入“ Abc”,在日期字段中选择“ 27/06/2018”,然后单击“提交”按钮,但是在服务器上,我意识到自定义日期时间模型绑定程序的代码正常运行调试时。

Screen

viewModel.FirstNested.SecondNested.ReleaseDate bound when debugging

问题

能否请您帮我找出为什么在上述情况1中自定义日期模型联编程序无法运行的原因?

非常感谢

0 个答案:

没有答案