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?
W
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/
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)#>);
<#+
}
#>
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();
}