Trước C# 10, để lưu trữ data thời gian, bạn sẽ dùng DateTime. Đây là kiểu vừa lưu trữ được Date vừa lưu trữ được Time.
Vấn đề xảy ra là có rất nhiều kiểu dữ liệu liên quan tới thời gian nhưng không sử dụng tới Time như: birthday, anniversary …
Và cũng có nhiều trường hợp bạn muốn lưu trữ tới Time mà không muốn kèm Date phía trước.
Ví dụ như dữ liệu chứng khoán, nếu data trong ngày quá lớn, mà bạn lưu trữ hàng triệu record với 1 date hơi dư thừa, chắc chắn sẽ làm chậm trong quá trình xử lý hoặc tốn không gian lưu trữ.
Vấn đề này đã được giải quyết. .NET 6/C# 10 đã giới thiệu 2 cấu trúc mới là DateOnly và TimeOnly.
DateOnly
public DateOnly (int year, int month, int day);
VD:
var dateOnly = new DateOnly(2022, 1, 1);
Lưu ý: Đối tượng DateOnly không chứa Timezone.
TimeOnly
public TimeOnly (int hour, int minute);
VD:
TimeOnly time = TimeOnly.MinValue;
Console.WriteLine(time); //Outputs 12:00 AM
Giả sử bạn khai báo biến startTime = 11PM và cộng thêm 2h.
TimeOnly startTime = TimeOnly.Parse("11:00 PM");
var hoursWorked = 2;
var endTime = startTime.AddHours(hoursWorked);
Console.WriteLine(endTime); //Outputs 1:00 AM
Đối với TimeSpan, bạn sẽ ra kết quả 1 ngày 1h
Ngoài ra có 1 hàm vô cùng hữu ích IsBetween(), để xem 1 đối tượng TimeOnly có nằm trong khoảng thời gian xác định hay không.
TimeOnly startTime = TimeOnly.Parse("11:00 PM");
var hoursWorked = 2;
var endTime = startTime.AddHours(hoursWorked);
var isBetween = TimeOnly.Parse("12:00 AM").IsBetween(startTime, endTime); //Returns true.
Sử dụng DateOnly và TimeOnly với SQL Server
Theo dự kiến, tới .NET 7, Micorosoft sẽ hỗ trợ binding DateOnly và TimeOnly với SQL Server.
Như mình ví dụ lúc đầu, sẽ rất tốn thời gian và dung lượng để lưu trữ DateTime xuống SQL Server khi đối tượng đó chỉ cần Date hoặc Time.
Để giải quyết vấn đề đó, mình sử dụng thư viện TinyHelpers:
https://github.com/marcominerva/TinyHelpers
Để sử dụng thư viện, đầu tiên bạn cần cài đặt TinyHelpers từ Nuget. Sau đó trong DbContext, bạn override lại như ví dụ sau:
// using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(builder =>
{
// Date is a DateOnly property (.NET 6)
builder.Property(x => x.Date).HasDateOnlyConversion();
// Time is a TimeOnly property (.NET 6)
builder.Property(x => x.Time).HasTimeOnlyConversion();
// Comments is a complex type, this Converter will automatically JSON-de/serialize it
// in a string column.
builder.Property(x => x.Comments).HasJsonConversion();
});
}
Ví dụ
Bạn viết chương trình để lưu thông tin người dùng và thời gian ngủ-thức dậy của người đó hàng ngày.
Tạo 2 bảng People và WakeupTime sử dụng DateOnly và TimeOnly
- Birthday: DateOnly
- BedTime: TimeOnly
- WakeupTime: TimeOnly
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateOnly Birthday { get; set; }
public List<WakeUpTime> WakeUpTimes { get; set; }
}
Khai báo entity WakeupTime
public class WakeUpTime
{
public int Id { get; set; }
public int PersonId { get; set; }
public TimeOnly WakeupTime { get; set; }
public TimeOnly BedTime { get; set; }
[ForeignKey("PersonId")]
public virtual Person Person { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime LastModifiedDate { get; set; }
}
Trong DbContext, bạn nhớ sử dụng hàm converter()
public class WatchContext: DbContext
{
public WatchContext(DbContextOptions<WatchContext> options)
: base(options)
{ }
public DbSet<Person> People { get; set; }
public DbSet<WakeUpTime> WakeUpTime { get; set; }
// using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>(builder =>
{
// Date is a DateOnly property (.NET 6)
builder.Property(x => x.Birthday).HasDateOnlyConversion();
});
modelBuilder.Entity<WakeUpTime>(builder =>
{
// Date is a TimeOnly property (.NET 6)
builder.Property(x => x.BedTime).HasTimeOnlyConversion();
builder.Property(x => x.WakeupTime).HasTimeOnlyConversion();
});
}
}
MainProgram.cs:
static void DemoConnectDatabase()
{
DemoWakeupTime();
GetPerson();
}
static void DemoWakeupTime()
{
var name = "Franklin Delano Roosevelt";
var factory = new WatchContextFactory();
using (var context = factory.CreateDbContext(null))
{
var findPerson = context.People.FirstOrDefault(t => t.Name == name);
if (findPerson == null)
{
findPerson = new Person
{
Name = "Franklin Delano Roosevelt",
Birthday = new DateOnly(1989, 01, 10)
};
context.People.Add(findPerson);
context.SaveChanges();
var wakeUpTime = new WakeUpTime
{
PersonId = findPerson.Id,
BedTime = new TimeOnly(21, 0),
WakeupTime = new TimeOnly(6, 0),
CreatedDate = DateTime.Now,
LastModifiedDate = DateTime.Now,
};
context.WakeUpTime.Add(wakeUpTime);
context.SaveChanges();
}
}
}
static void GetPerson()
{
var factory = new WatchContextFactory();
using (var context = factory.CreateDbContext(null))
{
var findPerson = context.People.FirstOrDefault();
if (findPerson != null)
{
Console.WriteLine($"Name: {findPerson.Name}");
var wakeupTime = context.WakeUpTime.Where(t=>t.PersonId == findPerson.Id).ToList();
var badSleep = new BadSleepApprover();
var goodSleep = new GoodSleepApprover();
var notGoodSleep = new NotGoodSleepApprover();
badSleep.Next(goodSleep);
goodSleep.Next(notGoodSleep);
foreach (var item in wakeupTime)
{
var sleepTime = item.WakeupTime - item.BedTime;
badSleep.HandleRequest(sleepTime);
}
}
}
}
Link download: https://www.mediafire.com/file/nn9fev6xwxllb1q
Chúc các bạn thành công!!!
Nhận xét
Đăng nhận xét