ZorgoZ
ZorgoZ

Reputation: 3569

Problem binding DateOnly in ASP.NET Core 6 MVC

I was trying to use the new DateOnly type in .NET 6, but I had trouble with the model binding. A perfectly normal form data such as this:

enter image description here

was parsed to "0001.01.01" and accepted as valid by the binder.

Replacing all DataOnly with the good old DateTime instantly solved this issue.

Could this be a bug in the BCL or am I missing some intentions here? Is there any workaround except for implementing a custom model binder?

Upvotes: 14

Views: 12747

Answers (4)

stanimirsp
stanimirsp

Reputation: 3076

There is already support for DateOnly and TimeOnly for System.Text.Json (STJ). And because the model binder uses STJ, after updating your NuGet package DateOnly will work like a charm.

Upvotes: 1

Sujoy
Sujoy

Reputation: 1207

Instead of using DateOnly like the following:

[Required(ErrorMessage = "{0} is required")]
public DateOnly TravelDate { get; set; }

You can use string datatype in the RequestDTO object accepted by API Controller as parameter:

[Required(ErrorMessage = "{0} is required")]
public string TravelDate { get; set; }

In the frontend you can use some masked edit control to accept date as string in some consistent format like "d-m-yyyy". Then you can use the following syntax to map it to the model which will be inserted into database:

model.TravelDate = DateOnly.ParseExact(dto.TravelDate, "d-M-yyyy"))

If you are using Dapper to commit the model(containing the DateOnly property) then you have to use a mapper:

public class SqlDateOnlyTypeHandler : SqlMapper.TypeHandler<DateOnly>
{
    public override void SetValue(IDbDataParameter parameter, DateOnly date)
    {
        parameter.Value = date.ToString();
    }
    public override DateOnly Parse(object value)
    {
        return DateOnly.FromDateTime((DateTime)value);
    }
}

Don't forget to register the mapper class in Program.cs like the following:

SqlMapper.AddTypeHandler(new SqlDateOnlyTypeHandler());

Upvotes: 1

Justin Erdmier
Justin Erdmier

Reputation: 43

As stated in the prior answer, full binding support is not available yet for DateOnly. However, I had a similar issue and was able to solve this using the DataType annotation.

I am working with an API that only requires the date and did not want to go through the hassle of getting the date from DateTime (albeit it's not much of a hassle, but still...).

This is the property on my model I use to build objects from the data I get back from the API:

[DataType(DataType.Date)]
public DateTime DateBecameLead { get; init; }

This is the property on my Index.cshtml.cs I use to filter the list of opportunity cards by date.:

[ BindProperty(SupportsGet = true), DataType(DataType.Date) ]
public DateTime DateBecameLead { get; init; } = new (2021, 1, 1);

Then, further down Index.cshtml.cs, I can easily compare the two properties:

OpportunityCards = OpportunityCards.Where(opp => opp.DateBecameLead >= DateBecameLead).ToList();

And for further context, here's the input tag on the Index.cshtml that is binded to the property from Index.cshtml.cs:

<input id="date-became-lead"
       type="date"
       asp-for="DateBecameLead" />

It's still using the old DateTime object, however, it does give the same benefit as using the DateOnly object and works just as expected. Once the full binding support is given to DateOnly, the code won't change much except I'll be able to live without the annotations.

Upvotes: 3

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131190

Full binding support for DateOnly and TimeOnly isn't available yet. Another related issue is this one. This is planned for .NET 7

The workaround in the second issue is to create custom JsonConverter and TypeConverters. The creator of the second issue has packaged both in the DateOnlyTimeOnly.AspNet package

Upvotes: 14

Related Questions