استفادهی گسترده از DateTimeOffset در NET Core.
نویسنده: وحید نصیری
تاریخ: ۱۳۹۵/۰۹/۲۸ ۱۲:۱۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public virtual DateTime? LockoutEndDateUtc { get; set; } public virtual DateTimeOffset? LockoutEnd { get; set; } 2012-03-01 00:00:00-05:00
var dateString = "2012-03-01 00:00:00-05:00"; var birthDay = DateTime.Parse(dateString);
2012-02-29 11:00:00 PM
string birthDay = "2012-03-01 00:00:00-05:00"; var dtOffset = DateTimeOffset.Parse(birthDay);
var theDay = dtOffset.Date;
public static class SqlDataReaderExtensions
{
public static DateTime GetDateTimeUtc(this SqlDataReader reader, string name)
{
int fieldOrdinal = reader.GetOrdinal(name);
DateTime unspecified = reader.GetDateTime(fieldOrdinal);
return DateTime.SpecifyKind(unspecified, DateTimeKind.Utc);
}
} ((DateTimeOffset)value).Ticks.ToString()
namespace MySQLite
{
public class SQLiteDbContext : DbContext
{
public SQLiteDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
addDateTimeOffsetConverter(builder);
}
private static void addDateTimeOffsetConverter(ModelBuilder builder)
{
// SQLite does not support DateTimeOffset
foreach (var property in builder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(DateTimeOffset)))
{
property.SetValueConverter(
new ValueConverter<DateTimeOffset, DateTime>(
convertToProviderExpression: dateTimeOffset => dateTimeOffset.UtcDateTime,
convertFromProviderExpression: dateTime => new DateTimeOffset(dateTime)
));
}
foreach (var property in builder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(DateTimeOffset?)))
{
property.SetValueConverter(
new ValueConverter<DateTimeOffset?, DateTime>(
convertToProviderExpression: dateTimeOffset => dateTimeOffset.Value.UtcDateTime,
convertFromProviderExpression: dateTime => new DateTimeOffset(dateTime)
));
}
}
}
} // یک نمونهی دیگر
private static readonly ValueConverter<object, string> DateTimeOffsetToStringConverter =
new ValueConverter<object, string>(
v => ((DateTimeOffset)v).ToString(@"yyyy\-MM\-dd HH\:mm\:ss.FFFFFFFzzz", CultureInfo.InvariantCulture),
v => DateTimeOffset.Parse(v, CultureInfo.InvariantCulture)); var dateTimeConverter = new ValueConverter<DateTime, DateTime>(
v => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime(),
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
var nullableDateTimeConverter = new ValueConverter<DateTime?, DateTime?>(
v => !v.HasValue ? v : (v.Value.Kind == DateTimeKind.Utc ? v : v.Value.ToUniversalTime()),
v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : v);
foreach (var property in builder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties()))
{
if (property.ClrType == typeof(DateTime))
{
property.SetValueConverter(dateTimeConverter);
}
if (property.ClrType == typeof(DateTime?))
{
property.SetValueConverter(nullableDateTimeConverter);
}
} نگارش بهروز «یک نکتهی تکمیلی: استفاده از DateTime.UtcNow و EF Core»
به صورت خلاصه اگر در بانک اطلاعاتی خود، اطلاعات زمانی را برای مثال توسط DateTime.UtcNow ذخیره میکنید، هرچند DateTimeKind آن به Utc تنظیم میشود، اما هنگام دریافت مقدار آن از بانک اطلاعاتی، این DateTimeKind بازیابی نشده (ADO.NET چنین قابلیتی را ندارد) و ... شاهد ناهماهنگیهایی خواهیم بود. برای یکدست سازی برنامه در این حالت میتوان از تبدیلگرهای زیر استفاده کرد (برای دو حالت DateTime و ?DateTime):
public class NullableDateTimeAsUtcValueConverter() : ValueConverter<DateTime?, DateTime?>(
v => !v.HasValue ? v : ToUtc(v.Value), v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : v)
{
private static DateTime? ToUtc(DateTime v) => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime();
}
public class DateTimeAsUtcValueConverter() : ValueConverter<DateTime, DateTime>(
v => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime(), v => DateTime.SpecifyKind(v, DateTimeKind.Utc));که اینبار و در نگارشهای جدیدتر EF، روش معرفی سراسری آنها به صورت زیر ساده میشود:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);
configurationBuilder.Properties<DateTime>().HaveConversion<DateTimeAsUtcValueConverter>();
configurationBuilder.Properties<DateTime?>().HaveConversion<NullableDateTimeAsUtcValueConverter>();
} DateTimeOffsetToBinaryConverter - DateTimeOffset to binary-encoded 64-bit value (stores it as a long, slight reduction in precision)
DateTimeOffsetToBytesConverter - DateTimeOffset to byte array (stores it as a 12 byte array, 8 bytes for time, 4 bytes for offset. Full precision.)
DateTimeOffsetToStringConverter - DateTimeOffset to string (ISO 8601 string including timezone)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
{
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
// To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset
// use the DateTimeOffsetToBinaryConverter
// Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754
// This only supports millisecond precision, but should be sufficient for most use cases.
foreach (var entityType in builder.Model.GetEntityTypes())
{
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset));
foreach (var property in properties)
{
builder
.Entity(entityType.Name)
.Property(property.Name)
.HasConversion(new DateTimeOffsetToBinaryConverter());
}
}
}
}