EF Core - Thuộc tính Shadow trong EF Core
Thuộc Tính Shadow trong Entity Framework Core
Entity Framework Core đã giới thiệu một loại thuộc tính mới được gọi là thuộc tính "Shadow" không tồn tại trong EF 6.x.
Thuộc tính Shadow là các thuộc tính không được xác định trực tiếp trong lớp thực thể .NET; thay vào đó, Chúng ta định cấu hình nó cho loại thực thể cụ thể trong mô hình dữ liệu thực thể. Chúng có thể được cấu hình trong phương thức OnModelCreating () của lớp Context.
Xem hình sau đây:
Chúng ta có thể thấy trong hình trên, thuộc tính shadow không phải là một phần của lớp thực thể của bạn. Vì vậy, chúng ta không thể truy cập nó khi bạn truy cập các thuộc tính khác của một thực thể. Thuộc tính Shadow chỉ có thể được định cấu hình cho một loại thực thể trong khi xây dựng Mô hình Dữ liệu Thực thể và chúng cũng được ánh xạ tới một cột cơ sở dữ liệu. Giá trị và trạng thái của các thuộc tính bóng được duy trì hoàn toàn trong Trình theo dõi thay đổi.
Giả sử rằng chúng ta cần bảo trì ngày được tạo và cập nhật của mỗi bản ghi trong bảng cơ sở dữ liệu. Bạn đã học cách đặt ngày được tạo và sửa đổi của các thực thể trong EF Core bằng cách định nghĩa các thuộc tính CreatedDate và UpdatedDate trong các lớp. Ở đây, chúng ta sẽ thấy cách đạt được kết quả tương tự bằng cách sử dụng các thuộc shadow mà không đưa chúng vào các lớp.
Xem xét lớp thực thể Student sau đây.
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public DateTime? DateOfBirth { get; set; }
public decimal Height { get; set; }
public float Weight { get; set; }
}
Lớp Student trên không chứa các thuộc tính CreatedDate và UpdatedDate để bảo trì thời gian đã tạo hoặc cập nhật. Chúng ta sẽ định cấu hình chúng dưới dạng thuộc shadow trên lớp Student.
Định nghĩa thuộc tính Shadow
Chúng ta có thể định nghĩa các tính shadow cho một loại thực thể sử dụng Fluent API OnModelCreating () sử dụng phương thức Property().
Dưới đây cấu hình hai thuộc tính shadow CreatedDate và UpdatedDate trên lớp Student.
public class SchoolContext : DbContext
{
public SchoolContext() : base()
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Student>().Property<DateTime>("CreatedDate");
modelBuilder.Entity<Student>().Property<DateTime>("UpdatedDate");
}
public DbSet<Student> Students { get; set; }
}
Phương thức Property () được sử dụng để định cấu hình thuộc tính shadow. Tên của thuộc tính bóng dưới dạng một chuỗi và loại dưới dạng tham số chung. Nếu tên được khai báo trong phương thức Property()() khớp với tên của thuộc tính hiện có, thì EF Core sẽ định cấu hình thuộc tính hiện có đó làm thuộc tính shadow thay vì giới thiệu thuộc tính bóng mới.
Thuộc tính Shadow trong Cơ sở dữ liệu
Khi chúng ta khai báo thuộc shadow, chúng ta cần cập nhật lược đồ cơ sở dữ liệu vì các thuộc tính shadow sẽ được ánh xạ tới cột cơ sở dữ liệu tương ứng.
Để thực hiện việc này, hãy thêm add database migration bằng lệnh sau trong Package Manager Console in Visual Studio.
PM> add-migration <migration-name>
PM> update-database
Bây giờ, Bảng Student
chứa 2 cột , CreatedDate
và UpdatedDate
trong SQL Server, xem hình bên dưới:
Do đó, cơ sở dữ liệu sẽ có các cột tương ứng ngay cả khi chúng ta chưa khai báo các thuộc tính này vào lớp Student và cấu hình chúng dưới dạng thuộc tính shadow.
Truy cập thuộc tính Shadow
Chúng ta có thể lấy hoặc gắn các giá trị của thuộc tính shadow bằng cách sử dụng phương thức Property () của EntityEntry. Đoạn mã sau truy cập giá trị của thuộc tính shadow.
using (var context = new SchoolContext())
{
var std = new Student(){ StudentName = "Bill" };
// sets the value to the shadow property
context.Entry(std).Property("CreatedDate").CurrentValue = DateTime.Now;
// gets the value of the shadow property
var createdDate = context.Entry(std).Property("CreatedDate").CurrentValue;
}
Tuy nhiên, chúng ta muốn gán giá trị cho các thuộc tính shadow này tự động trong phương thức SaveChanges (), không phải đặt chúng theo cách thủ công trên từng đối tượng. Vì vậy, ghi đè phương thức SaveChanges() trong lớp context, như được hiển thị bên dưới.
public override int SaveChanges()
{
var entries = ChangeTracker
.Entries()
.Where(e =>
e.State == EntityState.Added
|| e.State == EntityState.Modified);
foreach (var entityEntry in entries)
{
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
if (entityEntry.State == EntityState.Added)
{
entityEntry.Property("CreatedDate").CurrentValue = DateTime.Now;
}
}
return base.SaveChanges();
}
Nó sẽ tự động gán các giá trị thành thuộc tính shadow CreatedDate và UpdatedDate.
Bây giờ, hãy thực thi đoạn mã sau và kiểm tra bản ghi trong cơ sở dữ liệu.
using (var context = new SchoolContext())
{
var std = new Student(){ StudentName = "Bill" };
context.Add(std);
context.SaveChanges();
}
Đoạn mã trên sẽ chèn bản ghi sau với CreatedDate
và UpdatedDate
nhật trong bảng Students
.
Do đó, bằng cách cấu hình các thuộc tính shadow, chúng ta không cần phải khai báo chúng vào trong lớp.
Cấu hình thuộc tính shadow trên tất cả các lớp
Chúng ta thể định cấu hình các thuộc tính shadow trên tất cả các lớp cùng một lúc, thay vì định cấu hình chúng theo cách thủ công cho tất cả.
Ví dụ: chúng ta có thể định cấu hình CreatedDate
và UpdatedDate
trên tất cả các thực thể cùng một lúc, như được hiển thị bên dưới.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var allEntities = modelBuilder.Model.GetEntityTypes();
foreach (var entity in allEntities)
{
entity.AddProperty("CreatedDate",typeof(DateTime));
entity.AddProperty("UpdatedDate",typeof(DateTime));
}
}
Khi nào sử dụng thuộc tính shadow?
Thuộc tính shadow có thể được sử dụng trong hai trường hợp:
- Khi chúng ta không muốn hiển thị các cột cơ sở dữ liệu trên các lớp được ánh xạ, chẳng hạn như tình huống được thảo luận ở trên.
- Khi chúng ta không muốn để lộ thuộc tính khóa ngoại và chỉ muốn quản lý mối quan hệ bằng cách sử dụng thuộc tính điều hướng. Thuộc tính khóa ngoại sẽ là thuộc tính shadow và được ánh xạ tới cột cơ sở dữ liệu nhưng sẽ không được hiển thị dưới dạng thuộc tính của một thực thể. (Trong EF Core, nếu bạn không xác định thuộc tính khóa ngoại trong các lớp thực thể thì nó sẽ tự động tạo thuộc tính shadow cho điều đó. Bạn không cần phải định cấu hình thuộc tính khóa ngoại theo cách thủ công.)