Inilah satu pendekatan yang dapat Anda pertimbangkan:
Pertama, tentukan atribut berikut ini:
[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));
}
}
}
Sekarang kaitkan atribut itu ke konteks EF Anda:
public class MyContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public MyContext()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
(sender, e) => DateTimeKindAttribute.Apply(e.Entity);
}
}
Sekarang di salah satu DateTime
atau DateTime?
properti, Anda dapat menerapkan atribut ini:
public class Foo
{
public int Id { get; set; }
[DateTimeKind(DateTimeKind.Utc)]
public DateTime Bar { get; set; }
}
Dengan ini, setiap kali Entity Framework memuat entitas dari database, itu akan mengatur DateTimeKind
yang Anda tentukan, seperti UTC.
Perhatikan bahwa ini tidak melakukan apa pun saat menyimpan. Anda masih harus memiliki nilai yang dikonversi dengan benar ke UTC sebelum Anda mencoba menyimpannya. Tapi itu memungkinkan Anda untuk mengatur jenis saat mengambil, yang memungkinkan untuk diserialkan sebagai UTC, atau diubah ke zona waktu lain dengan TimeZoneInfo
.
'System.Array' does not contain a definition for 'Where'
Saya sangat menyukai pendekatan Matt Johnson, tetapi dalam model saya SEMUA anggota DateTime saya adalah UTC dan saya tidak ingin menghiasi semuanya dengan atribut. Jadi saya menggeneralisasi pendekatan Matt untuk memungkinkan event handler menerapkan nilai Kind default kecuali jika anggota secara eksplisit didekorasi dengan atribut tersebut.
Konstruktor untuk kelas ApplicationDbContext menyertakan kode ini:
DateTimeKindAttribute
terlihat seperti ini:sumber
DateTIme
atribut tanpa metode penyetel (publik). Edit yang disarankan. Lihat juga stackoverflow.com/a/3762475/2279059Jawaban yang diterima tidak berfungsi untuk objek Proyeksi atau Anonim. Performa juga bisa menjadi masalah.
Untuk mencapai ini, kita perlu menggunakan
DbCommandInterceptor
, sebuah objek yang disediakan oleh EntityFramework.Buat Interceptor:
interceptionContext.Result
adalah DbDataReader, yang kita gantikan dengan milik kitaDaftarkan interseptor di file
DbConfiguration
Terakhir, daftarkan konfigurasi untuk di
DbContext
Itu dia. Bersulang.
Untuk kesederhanaan, berikut adalah keseluruhan implementasi DbReader:
sumber
Dispose
danGetData
?Saya yakin saya telah menemukan solusi yang tidak memerlukan pemeriksaan UTC atau manipulasi DateTime khusus.
Pada dasarnya Anda perlu mengubah entitas EF Anda untuk menggunakan datatype DateTimeOffset (BUKAN DateTime). Ini akan menyimpan zona waktu dengan nilai tanggal di database (SQL Server 2015 dalam kasus saya).
Saat EF Core meminta data dari DB, EF Core akan menerima info zona waktu juga. Ketika Anda meneruskan data ini ke aplikasi web (Angular2 dalam kasus saya) tanggal secara otomatis diubah ke zona waktu lokal browser yang saya harapkan.
Dan ketika dikirimkan kembali ke server saya, itu diubah menjadi UTC lagi secara otomatis, juga seperti yang diharapkan.
sumber
Untuk EF Core , ada diskusi bagus tentang topik ini di GitHub: https://github.com/dotnet/efcore/issues/4711
Solusi (kredit untuk Christopher Haws ) yang akan menghasilkan perlakuan terhadap semua tanggal saat menyimpannya ke / mengambilnya dari database sebagai UTC adalah dengan menambahkan yang berikut ini ke
OnModelCreating
metodeDbContext
kelas Anda :Juga, periksa tautan ini jika Anda ingin mengecualikan beberapa properti dari beberapa entitas agar tidak diperlakukan sebagai UTC.
sumber
IsQueryType
tampaknya telah digantikan olehIsKeyLess
: github.com/dotnet/efcore/commit/…Tidak ada cara untuk menentukan DataTimeKind di Entity Framework. Anda dapat memutuskan untuk mengubah nilai waktu tanggal ke utc sebelum menyimpan ke db dan selalu menganggap data yang diambil dari db sebagai UTC. Tapi objek DateTime yang dibuat selama kueri akan selalu "Tidak ditentukan". Anda juga bisa mengevaluasi menggunakan objek DateTimeOffset daripada DateTime.
sumber
Saya sedang meneliti ini sekarang, dan sebagian besar jawaban ini tidak terlalu bagus. Dari apa yang saya lihat, tidak ada cara untuk memberi tahu EF6 bahwa tanggal keluar dari database dalam format UTC. Jika demikian, cara termudah untuk memastikan properti DateTime model Anda dalam UTC adalah dengan memverifikasi dan mengonversi penyetel.
Berikut beberapa pseudocode mirip c # yang menjelaskan algoritme
Dua cabang pertama sudah jelas. Yang terakhir memegang saus rahasia.
Ketika EF6 membuat model dari data yang dimuat dari database, DateTimes adalah
DateTimeKind.Unspecified
. Jika Anda tahu tanggal Anda semua UTC di db, maka cabang terakhir akan bekerja dengan baik untuk Anda.DateTime.Now
selaluDateTimeKind.Local
, jadi algoritme di atas berfungsi dengan baik untuk tanggal yang dihasilkan dalam kode. Sebagian besar waktu.Namun, Anda harus berhati-hati karena ada cara lain untuk
DateTimeKind.Unspecified
menyelinap ke dalam kode Anda. Misalnya, Anda mungkin deserialize model Anda dari data JSON, dan default flavor deserializer Anda untuk jenis ini. Terserah Anda untuk berhati-hati terhadap tanggal lokal yang ditandaiDateTimeKind.Unspecified
dari mendapatkan setter itu dari siapa pun kecuali EF.sumber
DateTimeKind.Utc
setelah hasil Anda dibuat. Contoh:from o in myContext.Records select new DTO() { BrokenTimestamp = o.BbTimestamp };
menyetel semua Jenis keDateTimeKind.Unspecified
.Jika Anda berhati-hati untuk meneruskan tanggal UTC dengan benar saat Anda menetapkan nilai dan semua yang Anda pedulikan adalah memastikan DateTimeKind diatur dengan benar saat entitas diambil dari database, lihat jawaban saya di sini: https://stackoverflow.com/ a / 9386364/279590
sumber
Setahun lagi, solusi lain! Ini untuk EF Core.
Saya memiliki banyak
DATETIME2(7)
kolom yang dipetakanDateTime
, dan selalu menyimpan UTC. Saya tidak ingin menyimpan offset karena jika kode saya benar maka offset akan selalu nol.Sementara itu saya memiliki kolom lain yang menyimpan nilai tanggal-waktu dasar dari offset yang tidak diketahui (disediakan oleh pengguna), jadi mereka hanya disimpan / ditampilkan "sebagaimana adanya", dan tidak dibandingkan dengan apa pun.
Oleh karena itu saya membutuhkan solusi yang dapat saya terapkan pada kolom tertentu.
Tentukan metode ekstensi
UsesUtc
:Ini kemudian dapat digunakan pada properti dalam pengaturan model:
Ini memiliki keuntungan kecil atas atribut yang hanya dapat Anda terapkan ke properti dengan tipe yang benar.
Perhatikan bahwa ini mengasumsikan nilai-nilai dari DB dalam UTC tetapi hanya salah
Kind
. Oleh karena itu, ini mengatur nilai yang Anda coba simpan di DB, memberikan pengecualian deskriptif jika bukan UTC.sumber
Bagi mereka yang perlu mencapai solusi @MattJohnson dengan .net framework 4 seperti saya, dengan batasan sintaks / metode refleksi, diperlukan sedikit modifikasi seperti yang tercantum di bawah ini:
sumber
Solusi Matt Johnson-Pint berfungsi, tetapi jika semua DateTimes Anda seharusnya UTC, membuat atribut akan terlalu berputar-putar. Inilah cara saya menyederhanakannya:
sumber
Pendekatan lain adalah membuat antarmuka dengan properti datetime, mengimplementasikannya pada kelas entitas parsial. Dan kemudian gunakan peristiwa SavingChanges untuk memeriksa apakah objek tersebut berjenis antarmuka, setel nilai waktu itu ke apa pun yang Anda inginkan. Faktanya, jika ini dibuat pada / diubah pada jenis tanggal, Anda dapat menggunakan acara itu untuk mengisinya.
sumber
Dalam kasus saya, saya hanya memiliki satu tabel dengan waktu UTC. Inilah yang saya lakukan:
sumber