Убедившись, что свойства DateTime возвращают DateTimeKind.Utc

Можно ли определить свойства DateTime в объектах сущностей Kind == DateTimeKind.Utc с помощью файла .edmx или шаблона t4?

Если возможно с использованием t4, опишите, как изменить свойство. В настоящее время свойство генерируется как:

[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();

person Sander Rijken    schedule 15.12.2009    source источник


Ответы (3)


В нашем случае было непрактично всегда указывать DateTimeKind, как указано ранее:

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

Если вы хотите, чтобы все объекты DateTime, выходящие из базы данных, были указаны как UTC, вам нужно добавить файл преобразования T4 и добавить дополнительную логику для всех объектов DateTime и DateTime, допускающих значение NULL, чтобы они инициализировались как DateTimeKind.Utc.

У меня есть сообщение в блоге, в котором это объясняется шаг за шагом: http://www.aaroncoleman.net/post/2011/06/16/Forcing-Entity-Framework-to-mark-DateTime-fields-at-UTC.aspx

Короче:

1) Создайте файл .tt для вашей модели .edmx.

2) Откройте файл .tt и найдите метод «WritePrimitiveTypeProperty».

3) Замените существующий код установки. Это все между обратными вызовами метода ReportPropertyChanging и ReportPropertyChanged со следующим:

<#+ 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)#>);
<#+ 
        }
#>
person aarondcoleman    schedule 16.06.2011
comment
Пытаюсь следить за вашим блогом и публиковать. Использование EF5 и 100% новое для T4. Не вижу тех же настроек, что и вы. Кроме того, уже созданы 2 файла TT, Entity.tt и Entity.Context.tt. Это все еще актуально или мне нужно найти другое решение? - person roadsunknown; 02.04.2013

Мое решение для обеспечения того, чтобы все значения DateTime читались как Utc DateTimes, выглядит следующим образом:

Я использовал тот же подход, что и Майкл (см. другой пост в блоге: https://stackoverflow.com/a/9386364/1069313 ) только тогда я нырнул немного глубже и использовал отражение для поиска DateTime и DateTime?

Сначала я написал три метода, которые находятся в моем классе методов DbContext Extensions. Потому что мне нужно использовать его для нескольких 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);
}

Затем я перехожу к конструктору моего WebsiteReadModelContext, который является объектом DbContext, и вызываю метод ReadAllDateTimeValuesAsUtc.

public WebsiteReadModelContext()
{
      this.ReadAllDateTimeValuesAsUtc();
}
person Jan Saris    schedule 27.07.2012
comment
Это просто спасло мою задницу. У меня были проблемы, когда мой сайт веб-API (с использованием ServiceStack.Text) возвращал даты в формате ISO8601, но без Z на конце для обозначения UTC. Моя база данных — это 100% UTC, так что это означало, что мне нужно было указать SpecifyKind для всех моих данных либо в моих сущностях, либо в моих контрактах сообщений. Это исправление сэкономило мне ЧАСЫ уродливого кодирования. Спасибо! - person RMD; 22.06.2013
comment
Недостатком этого подхода является то, что использование свойства ObjectContext фактически создает соединение с базой данных. По умолчанию EF6 пытается дождаться этого, пока данные не будут фактически прочитаны/записаны из/в базу данных. Есть ли способ сохранить это ленивое поведение? - person Brannon; 13.06.2014

Да, можно было бы использовать пользовательский шаблон T4.

Вам просто нужно настроить установщики и геттеры свойств.

Возможно, будет проще попробовать подход POCO;

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

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

person John Farrell    schedule 16.12.2009
comment
Каким образом настроить установщики/геттеры свойств? - person Sander Rijken; 17.12.2009