4

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();
Daniel Fortunov
  • 43,309
  • 26
  • 81
  • 106
Sander Rijken
  • 21,376
  • 3
  • 61
  • 85

3 Answers3

12

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();
}
Community
  • 1
  • 1
Jan Saris
  • 403
  • 4
  • 7
  • This just saved my butt. I was having issues where my Web API site (using ServiceStack.Text) was returning dates in ISO8601 format, but without the Z on the end to signify UTC. My database is 100% UTC, so this meant I needed to SpecifyKind on all my data either in my entities or in my message contracts. This fix saved me HOURS of ugly coding. Thanks! – RMD Jun 21 '13 at 23:43
  • One downside to this approach is that using the `ObjectContext` property actually instantiates the connection to the database. Out of the box EF6 tries to wait for that until data is actually read/written from/to the database. Is there some way to keep that lazy behavior? – Brannon Jun 13 '14 at 14:48
8

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)#>);
<#+ 
        }
#>
aarondcoleman
  • 536
  • 7
  • 9
  • 2
    Trying to follow your blog and post. Using EF5 and 100% new to T4. Do not see the same settings you are. Also, already have 2 TT files generated, Entity.tt and Entity.Context.tt. Is this still current or do I need to find a different solution? – roadsunknown Apr 02 '13 at 18:26
0

Yes, it would be possible to use a custom T4 template.

You'd just have to adjust your property setters and getters.

It might be easier to attempt a POCO approach;

For EF1: http://code.msdn.microsoft.com/EFPocoAdapter/Release/ProjectReleases.aspx?ReleaseId=1580

For EF4: http://thedatafarm.com/blog/data-access/agile-entity-framework-4-repository-part-1-model-and-poco-classes/

John Farrell
  • 24,673
  • 10
  • 77
  • 110