You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Razor Pages中DateOnly与TimeOnly绑定HTML日期时间控件问题

解决Razor Pages中DateOnly/TimeOnly与HTML日期时间控件的绑定问题

问题描述

使用MariaDB作为数据库,在Razor Pages中为用户提供日期选择器(type="date")和时间选择器(type="time")控件时,无法将DateOnly和TimeOnly字段正确绑定到这些HTML控件。

现有代码

视图代码

<form method="post">
<input hidden asp-for="Period.Periodid" />
<div class="border p-3 mt-4">
    <div class="row mb-3">
        <h4 class=" pl-3">Επεξεργασία Επαφής</h4>
    </div>
    <div asp-validation-summary="All" class="text-danger"></div>
    <div class="mb-3">
        <label asp-for="Period.StartDate">Ημερομηνία Έναρξης</label>
        <input type="date" asp-for="Period.StartDate" class="form-control" />
        <span asp-validation-for="Period.StartDate" class="text-danger"></span>
    </div>
    <div class="mb-3">
        <label asp-for="Period.EndDate">Ημερομηνία Λήξης</label>
        <input type="date" asp-for="Period.EndDate" class="form-control" />
        <span asp-validation-for="Period.EndDate" class="text-danger"></span>
    </div>
    <div class="mb-3">
        <label asp-for="Period.StartTime">Ώρα Έναρξης</label>
        <input type="time" asp-for="Period.StartTime" class="form-control" />
        <span asp-validation-for="Period.StartTime" class="text-danger"></span>
    </div>
    <div class="mb-3">
        <label asp-for="Period.EndTime">Ώρα Λήξης</label>
        <input type="time" asp-for="Period.EndTime" class="form-control" />
        <span asp-validation-for="Period.EndTime" class="text-danger"></span>
    </div>
    <button type="submit" class="btn btn-primary" style="width:150px;">Ενημέρωση</button>
    <a asp-page="Index" class="btn btn-secondary" style="width:150px;">Λίστα</a>
</div>

模型代码

public class Period
{
    public int Periodid { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateOnly StartDate { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateOnly EndDate { get; set; }

    [Required]
    [DataType(DataType.Time)]
    public TimeOnly StartTime { get; set; }

    [Required]
    [DataType(DataType.Time)]
    public TimeOnly EndTime { get; set; }
}

解决方案

1. 修正模型的DisplayFormat格式

HTML的date控件要求输入/输出的日期格式为yyyy-MM-dd(浏览器会自动转为本地格式显示,但绑定必须用标准格式);time控件要求格式为HH:mm:ss或HH:mm。修改模型的DisplayFormat:

public class Period
{
    public int Periodid { get; set; }

    [Required]
    [DataType(DataType.Date)]
    // 适配HTML date控件的标准格式
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateOnly StartDate { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateOnly EndDate { get; set; }

    [Required]
    [DataType(DataType.Time)]
    // 适配HTML time控件的标准格式
    [DisplayFormat(DataFormatString = "{0:HH:mm:ss}", ApplyFormatInEditMode = true)]
    public TimeOnly StartTime { get; set; }

    [Required]
    [DataType(DataType.Time)]
    [DisplayFormat(DataFormatString = "{0:HH:mm:ss}", ApplyFormatInEditMode = true)]
    public TimeOnly EndTime { get; set; }
}

2. 添加DateOnly/TimeOnly的模型绑定器(ASP.NET Core 6及更早版本)

ASP.NET Core 7+已原生支持DateOnly/TimeOnly的模型绑定,若使用更早版本,需手动实现绑定器:

第一步:注册绑定器

在Program.cs中添加:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.ModelBinderProviders.Insert(0, new DateOnlyModelBinderProvider());
        options.ModelBinderProviders.Insert(1, new TimeOnlyModelBinderProvider());
    });

第二步:实现绑定器类

// DateOnly绑定器提供类
public class DateOnlyModelBinderProvider : IModelBinderProvider
{
    public IModelBinder? GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType == typeof(DateOnly) || 
            context.Metadata.ModelType == typeof(DateOnly?))
        {
            return new DateOnlyModelBinder();
        }
        return null;
    }
}

// DateOnly绑定器
public class DateOnlyModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
        var value = valueProviderResult.FirstValue;

        if (string.IsNullOrEmpty(value))
        {
            bindingContext.Result = ModelBindingResult.Success(null);
            return Task.CompletedTask;
        }

        if (DateOnly.TryParse(value, CultureInfo.InvariantCulture, out var date))
        {
            bindingContext.Result = ModelBindingResult.Success(date);
        }
        else
        {
            bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "无效的日期格式");
        }

        return Task.CompletedTask;
    }
}

// TimeOnly绑定器提供类
public class TimeOnlyModelBinderProvider : IModelBinderProvider
{
    public IModelBinder? GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType == typeof(TimeOnly) || 
            context.Metadata.ModelType == typeof(TimeOnly?))
        {
            return new TimeOnlyModelBinder();
        }
        return null;
    }
}

// TimeOnly绑定器
public class TimeOnlyModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
        var value = valueProviderResult.FirstValue;

        if (string.IsNullOrEmpty(value))
        {
            bindingContext.Result = ModelBindingResult.Success(null);
            return Task.CompletedTask;
        }

        if (TimeOnly.TryParse(value, CultureInfo.InvariantCulture, out var time))
        {
            bindingContext.Result = ModelBindingResult.Success(time);
        }
        else
        {
            bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "无效的时间格式");
        }

        return Task.CompletedTask;
    }
}

3. 数据库层面适配(MariaDB)

确保EF Core将DateOnly映射到MariaDB的DATE类型,TimeOnly映射到TIME类型,在DbContext的OnModelCreating中配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Period>()
        .Property(p => p.StartDate)
        .HasColumnType("DATE");
    modelBuilder.Entity<Period>()
        .Property(p => p.EndDate)
        .HasColumnType("DATE");
    modelBuilder.Entity<Period>()
        .Property(p => p.StartTime)
        .HasColumnType("TIME");
    modelBuilder.Entity<Period>()
        .Property(p => p.EndTime)
        .HasColumnType("TIME");
}

验证

完成以上配置后,重新运行项目:

  • 日期选择器会正确显示DateOnly字段的值,提交后能正确绑定回模型
  • 时间选择器同理,可正常读取和提交TimeOnly值

内容的提问来源于stack exchange,提问作者snikos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:50:23