DateTime SpecifyKind in Entity Framework Core While Querying

DateTime SpecifyKind in Entity Framework Core While Querying

When you query an entity with a DateTime property via Entity Framework, Kind of DateTime properties are always Unspecified, because DateTime can not store time zone information in the database. However, we may want to use DateTime to store UTC dates in the database by convention and we may want to set Kind to UTC by default while querying from database.

In Entity Framework 6.x, we had ObjectMaterialized event to intercept querying and change Kind of DateTime properties. But Entity Framework Core does not provide a way of that. See related issue: https://github.com/aspnet/EntityFramework/issues/4711 It will be probably implemented in the future (https://github.com/aspnet/EntityFramework/issues/240), but we have a workaround.

1. Define a custom Entity Materializer Source:

    public class MyEntityMaterializerSource : EntityMaterializerSource
    {
        private static readonly MethodInfo NormalizeMethod = typeof(DateTimeMapper).GetTypeInfo().GetMethod(nameof(DateTimeMapper.Normalize));

        public override Expression CreateReadValueExpression(Expression valueBuffer, Type type, int index, IProperty property = null)
        {
            if (type == typeof(DateTime))
            {
                return Expression.Call(
                    NormalizeMethod,
                    base.CreateReadValueExpression(valueBuffer, type, index, property)
                );
            }

            return base.CreateReadValueExpression(valueBuffer, type, index, property);
        }
    }

2. Create a simple class to set kind of datetimes:

    public static class DateTimeMapper
    {
        public static DateTime Normalize(DateTime value)
        {
            return DateTime.SpecifyKind(value, DateTimeKind.Utc);
        }
    }

3. And finally replace  IEntityMaterializerSource by MyEntityMaterializerSource on DbContextOptionsBuilder:

DbContextOptions.ReplaceService<IEntityMaterializerSource, AbpEntityMaterializerSource>()

Then Entity Framework Core will use MyEntityMaterializerSource instead of default EntityMaterializerSource and MyEntityMaterializerSource will specify Kind of all DateTime’s to UTC.

Note: You probably want to handle nullable DateTime (DateTime?) properties too. In that case, remember to extend the code above to handle DateTime? too.

4 Comments
  • Mark
    Reply
    Posted at 07:38, May 4, 2017

    Great workaround. As a side note, you can override OnConfiguring in the context class to replace IEntityMaterializerSource:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    optionsBuilder.ReplaceService();
    }

  • Richard Beauchamp
    Reply
    Posted at 21:21, October 2, 2017

    Thanks so much for this post. Exactly what I needed!

Post a Comment

Comment
Name
Email
Website