wrz
4
2014
Dzisiaj natrafiłem na następujący problem: potrzebuję wyświetlać na ekranie daty dla użytkowników przekonwertowane na ich strefy czasowe. Każdy użytkownik ma zapisaną w bazie strefę czasową i ustawiam ją przy logowaniu (dla wygody trzymam w User.Identity). Daty w bazie danych są trzymane w formacie UTC.
Teraz, chcę aby dla każdego użytkownika daty wyświetlały mu się w jego strefie czasowej. Nie chcę tego robić na poziomie kontrolerów (aplikacja jest w MVC) ponieważ logika tam jest już zakodowana i zmiana jej wymagałaby dużo czasu. Chcę, aby konwersję przeprowadziło za mnie automatycznie EF. Z tym że nie wszystkie property DateTime na encjach powinny być w ten sposób konwertowane - jedynie te które chcę.
Po przeanalizowaniu wyników google znalazłem pewien pattern którym chcę się podzielić. W skrócie polega on na utworzeniu customowego atrybutu którym dekorujemy interesujące nas property na encji. Następnie, overridujemy zdarzenie OnMaterializing wywoływane przy utworzeniu obiektu encji i tam przez reflekcję odnajdujemy interesujące nas property i wykonujemy akcję.
Czy zaczynamy od stworzenia atrybutu:
[AttributeUsage(AttributeTargets.Property)]
public class ConvertDateTimeFromUTCToLocalAttribute : Attribute
{}
Następnie przechodzimy do naszego DataContextu i w konstruktorze umieszczamy kod:
public DatabaseContext( string connectionStringName )
: base( connectionStringName )
{
( (IObjectContextAdapter)this ).ObjectContext.ObjectMaterialized += ( s, e ) =>
{
var entity = e.Entity;
if( entity == null )
return;
foreach( var prop in entity.GetType().GetProperties().Where( p => p.GetCustomAttributes( typeof( ConvertDateTimeFromUTCToLocalAttribute ), true ).Count() == 1 ) )
{
var value = (DateTime?)prop.GetValue( entity );
if( value != null )
{
prop.SetValue( entity, value.Value.UTCToLocalTime() );
}
}
};
}
Teraz wystarczy na encji w której property chcemy "obsłużyć" dodać nasz atrybut:
[ConvertDateTimeFromUTCToLocal]
public DateTime? StartDate { get; set; }