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();
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();
}
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)#>);
<#+
}
#>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With