Aqui está uma abordagem que você pode considerar:
Primeiro, defina este atributo a seguir:
[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));
}
}
}
Agora conecte esse atributo ao seu contexto EF:
public class MyContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public MyContext()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
(sender, e) => DateTimeKindAttribute.Apply(e.Entity);
}
}
Agora, em qualquer propriedade DateTime
ou DateTime?
, você pode aplicar este atributo:
public class Foo
{
public int Id { get; set; }
[DateTimeKind(DateTimeKind.Utc)]
public DateTime Bar { get; set; }
}
Com isso em vigor, sempre que o Entity Framework carregar uma entidade do banco de dados, ele definirá o DateTimeKind
que você especificar, como UTC.
Observe que isso não faz nada ao salvar. Você ainda terá que converter o valor corretamente para UTC antes de tentar salvá-lo. Mas permite que você defina o tipo ao recuperar, o que permite que seja serializado como UTC ou convertido para outros fusos horários com TimeZoneInfo
.
'System.Array' does not contain a definition for 'Where'
Eu realmente gosto da abordagem de Matt Johnson, mas no meu modelo TODOS os meus membros DateTime são UTC e não quero ter que decorar todos eles com um atributo. Portanto, generalizei a abordagem de Matt para permitir que o manipulador de eventos aplique um valor padrão de Kind, a menos que um membro seja explicitamente decorado com o atributo.
O construtor da classe ApplicationDbContext inclui este código:
DateTimeKindAttribute
se parece com isso:fonte
DateTIme
atributo sem um método setter (público). Editar sugerido. Consulte também stackoverflow.com/a/3762475/2279059A resposta aceita não funciona para objetos projetados ou anônimos. O desempenho também pode ser um problema.
Para conseguir isso, precisamos usar a
DbCommandInterceptor
, um objeto fornecido por EntityFramework.Criar Interceptor:
interceptionContext.Result
é DbDataReader, que substituímos pelo nossoRegistre o interceptor em seu
DbConfiguration
Finalmente, registre a configuração para em seu
DbContext
É isso aí. Felicidades.
Para simplificar, aqui está toda a implementação do DbReader:
fonte
Dispose
eGetData
?Eu acredito que encontrei uma solução que não requer qualquer verificação UTC personalizada ou manipulação de DateTime.
Basicamente, você precisa alterar suas entidades EF para usar o tipo de dados DateTimeOffset (NOT DateTime). Isso armazenará o fuso horário com o valor de data no banco de dados (SQL Server 2015 no meu caso).
Quando o EF Core solicita os dados do banco de dados, ele também recebe as informações de fuso horário. Quando você passa esses dados para um aplicativo da web (Angular2 no meu caso), a data é automaticamente convertida para o fuso horário local do navegador, que é o que eu esperava.
E quando ele é passado de volta para o meu servidor, ele é convertido para UTC novamente de forma automática, também conforme o esperado.
fonte
Não há como especificar o DataTimeKind no Entity Framework. Você pode decidir converter os valores de data e hora em utc antes de armazenar em db e sempre assumir os dados recuperados de db como UTC. Mas os objetos DateTime materializados durante a consulta serão sempre "Não especificados". Você também pode avaliar usando o objeto DateTimeOffset em vez de DateTime.
fonte
Estou pesquisando isso agora, e a maioria dessas respostas não são exatamente boas. Pelo que posso ver, não há como dizer ao EF6 que as datas que saem do banco de dados estão no formato UTC. Se for esse o caso, a maneira mais simples de garantir que as propriedades DateTime do seu modelo estejam em UTC seria verificar e converter no configurador.
Aqui está um pseudocódigo semelhante ao c # que descreve o algoritmo
Os primeiros dois ramos são óbvios. O último contém o molho secreto.
Quando EF6 cria um modelo a partir de dados carregados do banco de dados, DateTimes são
DateTimeKind.Unspecified
. Se você sabe que suas datas estão todas UTC no banco de dados, o último branch funcionará muito bem para você.DateTime.Now
é sempreDateTimeKind.Local
, portanto, o algoritmo acima funciona bem para datas geradas no código. A maior parte do tempo.Você deve ter cuidado, no entanto, pois existem outras maneiras de
DateTimeKind.Unspecified
entrar furtivamente em seu código. Por exemplo, você pode desserializar seus modelos de dados JSON, e seu desserializador padrão para este tipo. Depende de você evitar que datas localizadas marcadasDateTimeKind.Unspecified
cheguem àquele setter de qualquer pessoa exceto EF.fonte
DateTimeKind.Utc
depois que os resultados forem gerados. Exemplo:from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };
define todos os tipos comoDateTimeKind.Unspecified
.Para EF Core , há uma grande discussão sobre este tópico no GitHub: https://github.com/dotnet/efcore/issues/4711
Uma solução (crédito para Christopher Haws ) que resultará no tratamento de todas as datas ao armazená-las / recuperá-las do banco de dados como UTC é adicionar o seguinte ao
OnModelCreating
método de suaDbContext
classe:Além disso, marque este link se desejar excluir algumas propriedades de algumas entidades de serem tratadas como UTC.
fonte
IsQueryType
parece ter sido substituído porIsKeyLess
: github.com/dotnet/efcore/commit/…Se você tiver o cuidado de passar as datas UTC corretamente ao definir os valores e tudo o que importa é garantir que DateTimeKind seja definido corretamente quando as entidades forem recuperadas do banco de dados, veja minha resposta aqui: https://stackoverflow.com/ a / 9386364/279590
fonte
Mais um ano, outra solução! Isso é para EF Core.
Tenho muitas
DATETIME2(7)
colunas que mapeiam paraDateTime
e sempre armazenam UTC. Não quero armazenar um deslocamento porque se meu código estiver correto, o deslocamento sempre será zero.Enquanto isso, tenho outras colunas que armazenam valores básicos de data e hora de deslocamento desconhecido (fornecidos pelos usuários), de modo que são armazenados / exibidos "como estão" e não são comparados com nada.
Portanto, preciso de uma solução que possa aplicar a colunas específicas.
Defina um método de extensão
UsesUtc
:Isso pode ser usado em propriedades na configuração do modelo:
Ele tem a pequena vantagem sobre os atributos de que você só pode aplicá-lo às propriedades do tipo correto.
Observe que ele assume que os valores do banco de dados estão em UTC, mas apenas estão errados
Kind
. Portanto, ele policia os valores que você tenta armazenar no BD, lançando uma exceção descritiva se eles não forem UTC.fonte
Para aqueles que precisam alcançar a solução @MattJohnson com .net framework 4 como eu, com sintaxe de reflexão / limitação de método, é necessária uma pequena modificação conforme listado abaixo:
fonte
A solução de Matt Johnson-Pint funciona, mas se todos os seus DateTimes deveriam ser UTC, criar um atributo seria muito tortuoso. Aqui está como eu simplifiquei:
fonte
Outra abordagem seria criar uma interface com as propriedades datetime e implementá-las nas classes de entidade parciais. Em seguida, use o evento SavingChanges para verificar se o objeto é do tipo de interface, defina esses valores de data e hora para o que quiser. Na verdade, se eles forem criados / modificados em datas, você pode usar esse evento para preenchê-los.
fonte
No meu caso, eu tinha apenas uma tabela com datetimes UTC. Aqui está o que eu fiz:
fonte