Home > .NET, C#, Entity Framework > DateOnly and TimeOnly support with Entity Framework Core 6.0

DateOnly and TimeOnly support with Entity Framework Core 6.0

Some times ago we talked about the missing support for DateOnly and TimeOnly types in System.Text.Json and we shown how to overcome this limitation.

Unfortunately, not even Entity Framework Core 6.0 on SQL Server supports these new data types. This happens because the underlying provider, Microsoft.Data.SqlClient, does not supported them neither, as described on a GitHub issue. The curios fact is that, instead, Microsoft.Data.Sqlite fully supports both DateOnly and TimeOnly, so we can use them with Entity Framework Core 6.0 and the SQLite provider.

But… What about SQL Server? In the case of System.Text.Json we developed two Converters to deserialize/deserialize these types. We can do the same thing with Entity Framework Core, using Values Converters:

public class DateOnlyConverter : ValueConverter<DateOnly, DateTime>
{
public DateOnlyConverter() : base(
dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue),
dateTime => DateOnly.FromDateTime(dateTime))
{
}
}
public class DateOnlyComparer : ValueComparer<DateOnly>
{
public DateOnlyComparer() : base(
(d1, d2) => d1.DayNumber == d2.DayNumber,
d => d.GetHashCode())
{
}
}
public class TimeOnlyConverter : ValueConverter<TimeOnly, TimeSpan>
{
public TimeOnlyConverter() : base(
timeOnly => timeOnly.ToTimeSpan(),
timeSpan => TimeOnly.FromTimeSpan(timeSpan))
{
}
}
public class TimeOnlyComparer : ValueComparer<TimeOnly>
{
public TimeOnlyComparer() : base(
(t1, t2) => t1.Ticks == t2.Ticks,
t => t.GetHashCode())
{
}
}

The DateOnlyConverter converts a DateOnly to DateTime (that is the corresponding type that Entity Framework Core actually supports) and vice versa. In the same way, TimeOnlyConverter converts a TimeOnly properties to and from a TimeSpan. In the code above we have defined also the Value Compares to correctly handle data comparison.

So, now we can easily enable support for DateOnly and TimeOnly properties in our classes, mapping columns that use the date and time types. We just need to add the converters and the comparers in the OnModelCreating method of the DbContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>(builder =>
    {
        // Date is a DateOnly property and date on database
        builder.Property(x => x.Date)
            .HasConversion<DateOnlyConverter, DateOnlyComparer>();

        // Time is a TimeOnly property and time on database
        builder.Property(x => x.Time)
            .HasConversion<TimeOnlyConverter, TimeOnlyComparer>();
    });
}

Note that, even with this solution, we still don’t have support for migrations, so if we try to create a migration for a class that contains a DateOnly or TimeOnly property, we’ll get an InvalidOperationException and, on the other hand, if we scaffold a table that has a date or time column, they will be mapped to DateTime and TimeSpan types, respectively.

If you don’t want to add these Converters and Comparers to all of your projects, you can use my small library, TinyHelpers.EntityFrameworkCore, that is available on GitHub and NuGet.

Categories: .NET, C#, Entity Framework

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: