Aquí hay un enfoque que podría considerar:
Primero, defina este siguiente atributo:
[AttributeUsage(AttributeTargets.Property)]
public class DateTimeKindAttribute : Attribute
{
private readonly DateTimeKind _kind;
public DateTimeKindAttribute(DateTimeKind kind)
{
_kind = kind;
}
public DateTimeKind Kind
{
get { return _kind; }
}
public static void Apply(object entity)
{
if (entity == null)
return;
var properties = entity.GetType().GetProperties()
.Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?));
foreach (var property in properties)
{
var attr = property.GetCustomAttribute<DateTimeKindAttribute>();
if (attr == null)
continue;
var dt = property.PropertyType == typeof(DateTime?)
? (DateTime?) property.GetValue(entity)
: (DateTime) property.GetValue(entity);
if (dt == null)
continue;
property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind));
}
}
}
Ahora conecte ese atributo a su contexto EF:
public class MyContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public MyContext()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
(sender, e) => DateTimeKindAttribute.Apply(e.Entity);
}
}
Ahora, en cualquier DateTime
o DateTime?
propiedades, se puede aplicar este atributo:
public class Foo
{
public int Id { get; set; }
[DateTimeKind(DateTimeKind.Utc)]
public DateTime Bar { get; set; }
}
Con esto en su lugar, cada vez que Entity Framework carga una entidad de la base de datos, establecerá el DateTimeKind
que especifique, como UTC.
Tenga en cuenta que esto no hace nada al guardar. Aún tendrá que convertir el valor correctamente a UTC antes de intentar guardarlo. Pero sí le permite establecer el tipo al recuperar, lo que permite serializarlo como UTC o convertirlo a otras zonas horarias con TimeZoneInfo
.
Matt Johnson-Pinta
fuente
'System.Array' does not contain a definition for 'Where'
Realmente me gusta el enfoque de Matt Johnson, pero en mi modelo TODOS mis miembros de DateTime son UTC y no quiero tener que decorarlos a todos con un atributo. Entonces generalicé el enfoque de Matt para permitir que el controlador de eventos aplique un valor Kind predeterminado a menos que un miembro esté explícitamente decorado con el atributo.
El constructor de la clase ApplicationDbContext incluye este código:
DateTimeKindAttribute
Se ve como esto:fuente
DateTIme
atributo sin un método de establecimiento (público). Se sugiere editar. Véase también stackoverflow.com/a/3762475/2279059La respuesta aceptada no funciona para objetos proyectados o anónimos. El rendimiento también podría ser un problema.
Para lograr esto, necesitamos usar a
DbCommandInterceptor
, un objeto proporcionado por EntityFramework.Crear interceptor:
interceptionContext.Result
es DbDataReader, que reemplazamos por el nuestroRegistre el interceptor en su
DbConfiguration
Finalmente, registre la configuración para en su
DbContext
Eso es. Salud.
Para simplificar, aquí está la implementación completa de DbReader:
fuente
Dispose
yGetData
?Creo que encontré una solución que no requiere ninguna verificación UTC personalizada o manipulación de DateTime.
Básicamente, debe cambiar sus entidades EF para usar el tipo de datos DateTimeOffset (NO DateTime). Esto almacenará la zona horaria con el valor de la fecha en la base de datos (SQL Server 2015 en mi caso).
Cuando EF Core solicita los datos de la base de datos, también recibirá la información de la zona horaria. Cuando pasa estos datos a una aplicación web (Angular2 en mi caso), la fecha se convierte automáticamente a la zona horaria local del navegador, que es lo que espero.
Y cuando se devuelve a mi servidor, se convierte a UTC nuevamente automáticamente, también como se esperaba.
fuente
No hay forma de especificar DataTimeKind en Entity Framework. Puede decidir convertir los valores de fecha y hora a utc antes de almacenarlos en db y siempre asumir que los datos recuperados de db son UTC. Pero los objetos DateTime materalizados durante la consulta siempre serán "Sin especificar". También puede evaluar usando el objeto DateTimeOffset en lugar de DateTime.
fuente
Estoy investigando esto en este momento, y la mayoría de estas respuestas no son exactamente buenas. Por lo que puedo ver, no hay forma de decirle a EF6 que las fechas que salen de la base de datos están en formato UTC. Si ese es el caso, la forma más sencilla de asegurarse de que las propiedades DateTime de su modelo estén en UTC sería verificar y convertir en el setter.
Aquí hay un pseudocódigo similar a c # que describe el algoritmo
Las dos primeras ramas son obvias. El último contiene la salsa secreta.
Cuando EF6 crea un modelo a partir de los datos cargados desde la base de datos, DateTimes son
DateTimeKind.Unspecified
. Si sabe que sus fechas son todas UTC en la base de datos, entonces la última rama funcionará muy bien para usted.DateTime.Now
es siempreDateTimeKind.Local
, por lo que el algoritmo anterior funciona bien para las fechas generadas en el código. La mayor parte del tiempo.Sin embargo, debe tener cuidado, ya que hay otras formas de
DateTimeKind.Unspecified
colarse en su código. Por ejemplo, puede deserializar sus modelos a partir de datos JSON, y su sabor de deserializador predeterminado es este tipo. Depende de usted protegerse contra las fechas localizadas marcadasDateTimeKind.Unspecified
para que no lleguen a ese colocador desde cualquier persona que no sea EF.fuente
DateTimeKind.Utc
después de que se generen los resultados. Ejemplo:from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };
establece todo Tipo enDateTimeKind.Unspecified
.Para EF Core , hay una gran discusión sobre este tema en GitHub: https://github.com/dotnet/efcore/issues/4711
Una solución (crédito a Christopher Haws ) que dará como resultado el tratamiento de todas las fechas al almacenarlas / recuperarlas de la base de datos como UTC es agregar lo siguiente al
OnModelCreating
método de suDbContext
clase:Además, consulte este enlace si desea excluir algunas propiedades de algunas entidades para que no se traten como UTC.
fuente
IsQueryType
parece haber sido reemplazado porIsKeyLess
: github.com/dotnet/efcore/commit/…Si tiene cuidado de pasar correctamente las fechas UTC cuando establece los valores y todo lo que le importa es asegurarse de que DateTimeKind esté configurado correctamente cuando las entidades se recuperan de la base de datos, vea mi respuesta aquí: https://stackoverflow.com/ a / 9386364/279590
fuente
¡Otro año, otra solución! Esto es para EF Core.
Tengo muchas
DATETIME2(7)
columnas que se asignanDateTime
y siempre almacenan UTC. No quiero almacenar un desplazamiento porque si mi código es correcto, el desplazamiento siempre será cero.Mientras tanto, tengo otras columnas que almacenan valores básicos de fecha y hora de desplazamiento desconocido (proporcionados por los usuarios), por lo que simplemente se almacenan / muestran "tal cual", y no se comparan con nada.
Por lo tanto, necesito una solución que pueda aplicar a columnas específicas.
Defina un método de extensión
UsesUtc
:Esto luego se puede usar en propiedades en la configuración del modelo:
Tiene la ventaja menor sobre los atributos de que solo puede aplicarlo a propiedades del tipo correcto.
Tenga en cuenta que asume que los valores de la base de datos están en UTC pero simplemente tienen el error
Kind
. Por lo tanto, controla los valores que intenta almacenar en la base de datos, lanzando una excepción descriptiva si no son UTC.fuente
Para aquellos que necesitan lograr la solución @MattJohnson con .net framework 4 como yo, con una limitación de sintaxis / método de reflexión, se requiere una pequeña modificación como se indica a continuación:
fuente
La solución de Matt Johnson-Pint funciona, pero si se supone que todos sus DateTimes son UTC, crear un atributo sería demasiado tortuoso. Así es como lo simplifiqué:
fuente
Otro enfoque sería crear una interfaz con las propiedades de fecha y hora, implementarlas en las clases de entidad parciales. Y luego use el evento SavingChanges para verificar si el objeto es del tipo de interfaz, establezca esos valores de fecha y hora en lo que desee. De hecho, si estos se crean / modifican en fechas determinadas, puede utilizar ese evento para completarlas.
fuente
En mi caso, solo tenía una tabla con fecha y hora UTC. Esto es lo que hice:
fuente