Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making sure that DateTime properties return DateTimeKind.Utc

Is it possible to define DateTime properties in entity objects that are of Kind == DateTimeKind.Utc by using either the .edmx file, or a t4 template?

When possible using t4, please describe how to change the property. Currently the property gets generated as:

[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.DateTime Created
{
    get
    {
        return _created;
    }
    internal set
    {
        OnCreatedChanging(value);
        ReportPropertyChanging("Created");
        _created = StructuralObject.SetValidValue(value);
        ReportPropertyChanged("Created");
        OnCreatedChanged();
    }
}
private global::System.DateTime _created;
partial void OnCreatedChanging(global::System.DateTime value);
partial void OnCreatedChanged();
like image 919
Sander Rijken Avatar asked Dec 01 '22 11:12

Sander Rijken


2 Answers

My solution to ensure that all the DateTime values are readed as Utc DateTimes is as followed:

I used the same approach as Michael (see other blog post: https://stackoverflow.com/a/9386364/1069313) only then I dived a little bit deeper, and used reflection to search for DateTime and DateTime?

First I wrote three methods which are in my DbContext Extensions methods class. Because I need to use it for multiple DbContexts

public static void ReadAllDateTimeValuesAsUtc(this DbContext context)
{
        ((IObjectContextAdapter)context).ObjectContext.ObjectMaterialized += ReadAllDateTimeValuesAsUtc;
}

private static void ReadAllDateTimeValuesAsUtc(object sender, ObjectMaterializedEventArgs e)
{
    //Extract all DateTime properties of the object type
    var properties = e.Entity.GetType().GetProperties()
        .Where(property => property.PropertyType == typeof (DateTime) ||
                           property.PropertyType == typeof (DateTime?)).ToList();
    //Set all DaetTimeKinds to Utc
    properties.ForEach(property => SpecifyUtcKind(property, e.Entity));
}

private static void SpecifyUtcKind(PropertyInfo property, object value)
{
    //Get the datetime value
    var datetime = property.GetValue(value, null);

    //set DateTimeKind to Utc
    if (property.PropertyType == typeof(DateTime))
    {
        datetime = DateTime.SpecifyKind((DateTime) datetime, DateTimeKind.Utc);
    }
    else if(property.PropertyType == typeof(DateTime?))
    {
        var nullable = (DateTime?) datetime;
        if(!nullable.HasValue) return;
        datetime = (DateTime?)DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc);
    }
    else
    {
        return;
    }

    //And set the Utc DateTime value
    property.SetValue(value, datetime, null);
}

And then I go to the constructor of my WebsiteReadModelContext which is a DbContext object and call the ReadAllDateTimeValuesAsUtc method

public WebsiteReadModelContext()
{
      this.ReadAllDateTimeValuesAsUtc();
}
like image 89
Jan Saris Avatar answered Dec 05 '22 23:12

Jan Saris


For our case it was impractical to always specify the DateTimeKind as stated previously:

DateTime utcDateTime = DateTime.SpecifyKind(databaseDateTime, DateTimeKind.Utc);

If you want to force all DateTime objects coming out of the database to be specified as UTC you'll need to add a T4 transform file and add additional logic for all DateTime and nullable DateTime objects such that they get initialized as DateTimeKind.Utc

I have a blog post which explains this step by step: http://www.aaroncoleman.net/post/2011/06/16/Forcing-Entity-Framework-to-mark-DateTime-fields-at-UTC.aspx

In short:

1) Create the .tt file for your .edmx model

2) Open the .tt file and find the "WritePrimitiveTypeProperty" method.

3) Replace the existing setter code. This is everything between the ReportPropertyChanging and the ReportPropertyChanged method callbacks with the following:

<#+ if( ((PrimitiveType)primitiveProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.DateTime)
            {
#>
        if(<#=code.FieldName(primitiveProperty)#> == new DateTime())
        {
            <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
<#+ 
            if(ef.IsNullable(primitiveProperty))
            {  
#>              
            if(value != null)
                <#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>.Value, DateTimeKind.Utc);
<#+             } 
            else
            {#>
            <#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>, DateTimeKind.Utc);                
<#+ 
            } 
#>
        }
        else
        {
            <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
        }
<#+ 
        }
        else
        {
#>
    <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
<#+ 
        }
#>
like image 39
aarondcoleman Avatar answered Dec 06 '22 00:12

aarondcoleman