Attach entity bị ngắt kết nối trong Entity Framework

Ở bài viết này bạn sẽ tìm hiểu về các phương thức khác nhau trong Entity Framework 6.x để gắn các biểu đồ thực thể bị ngắt kết nối vào một Context.

Lưu một thực thể trong kịch bản bị ngắt kết nối khác với kịch bản được kết nối. Có hai điều chúng ta cần làm khi nhận được biểu đồ thực thể bị ngắt kết nối hoặc thậm chí là một thực thể bị ngắt kết nối.

  • Đầu tiên, chúng ta cần đính kèm các thực thể với thể hiện của Context mới và làm cho Context biết về các thực thể này.
  • Thứ hai, thiết lập trạng thái EntityState phù hợp cho từng thực thể theo cách thủ công vì thể hiện của Context không biết gì về các thao tác được thực hiện trên các thực thể bị ngắt kết nối, do đó nó không thể tự động áp dụng trạng thái thích hợp cho EntityState.

Hình dưới đây minh họa quá trình này.

Đính kèm các thực thể bị ngắt kết nối vào context trong Entity Framework

Entity Framework cung cấp các phương thức sau để gắn các thực thể bị ngắt kết nối vào một Context và gán các trạng thái EntityState cho từng thực thể trong biểu đồ thực thể.

  • DbContext.Entry()
  • DbSet.Add()
  • DbSet.Attach()

Phương thức DbContext.Entry()

Phương thức Entry() của lớp DbContext trả về một thể hiện của DbEntityEntry cho thực thể được chỉ định.

Đối tượng DbEntityEntry cung cấp nhiều thông tin khác nhau về thực thể được chỉ định và khả năng thực hiện một hành động trên thực thể.

Quan trọng nhất, chúng ta có thể thay đổi trạng thái EntityState của thực thể được chỉ định bằng cách sử dụng thuộc tính State được trình bày ở bên dưới.

context.Entry(entity).state = EntityState.Added/Modified/Deleted

Phương thức Entry gắn toàn bộ biểu đồ thực thể đến một Context với trạng thái được chỉ định cho một thực thể cha và tất cả thực thể con của nó. Hãy xem ví dụ sau.

//Root entity (empty key)
var student = new Student() 
{ 
   StudentName = "Bill",
   StandardId = 1,
   Standard = new Standard()   //Child entity (with key value)
   {
        StandardId = 1,
        StandardName = "Grade 1"
   },
   Courses = new List<Course>() 
   {
        new Course()  //Child entity (empty key)
        {  
            CourseName = "Machine Language" 
        },
        new Course() //Child entity (with key value)
        {  
            CourseId = 2 
        }
    }
};

using (var context = new SchoolDBEntities())
{
    context.Entry(student).State = EntityState.Added; 

    foreach (var entity in context.ChangeTracker.Entries())
    {
        Console.WriteLine("{0}: {1}", entity.Entity.GetType().Name, entity.State);
    } 
}

Kết quả khi biên dịch và chạy chương trình:

Student: Added
Standard: Added
Course: Added
Course: Added

Trong ví dụ trên, biểu đồ thực thể của Student bao gồm các thực thể StandardCourse. Câu lệnh context.Entry(student).State = EntityState.Added; gán trạng thái Added cho thực thể cha cũng như cho tất cả các thực thể con bất kể thực thể con có chứa giá trị thuộc tính khóa trống hay không. Vì vậy, nên sử dụng phương thức Entry() một cách cẩn thận.

Bảng sau liệt kê hành vi của phương thức Entry().

Trạng thái thực thể cha Trạng thái của các thực thể con
Added Added
Modified Unchanged
Deleted Tất cả thực thể con sẽ là null

Phương thức DbSet.Add()

Phương thức DbSet.Add() gắn toàn bộ biểu đồ thực thể đến một Context và tự động áp dụng trạng thái Added cho tất cả các thực thể.

//disconnected entity graph
Student disconnectedStudent = new Student() 
{ 
    StudentName = "New Student" 
};
disconnectedStudent.StudentAddress = new StudentAddress() 
{ 
    Address1 = "Address", 
    City = "City1" 
};

using (var context = new SchoolDBEntities())
{
    context.Students.Add(disconnectedStudent);
                
    // get DbEntityEntry instance to check the EntityState of specified entity
    var studentEntry = context.Entry(disconnectedStudent);
    var addressEntry = context.Entry(disconnectedStudent.StudentAddress);

    Console.WriteLine("Student: {0}", studentEntry.State);
    Console.WriteLine("StudentAddress: {0}", addressEntry.State);
}

Kết quả khi biên dịch và chạy chương trình:

Student: Added
StudentAddress: Added

Phương thức DbSet.Add() gắn toàn bộ biểu đồ thực thể đến một Context với trạng thái Added cho từng đối tượng. Việc gọi phương thức context.Savechanges() sẽ thực thi lệnh INSERT cho tất cả các thực thể trong cơ sở dữ liệu.

Phương thức DbSet.Attach()

Phương thức DbSet.Attach() gắn toàn bộ biểu đồ thực thể đến một Context với trạng thái thực thể Unchanged.

//disconnected entity graph
Student disconnectedStudent = new Student() 
{ 
    StudentName = "New Student" 
};
disconnectedStudent.StudentAddress = new StudentAddress() 
{ 
    Address1 = "Address", 
    City = "City1" 
};

using (var context = new SchoolDBEntities())
{
    context.Students.Attach(disconnectedStudent);
                
    // get DbEntityEntry instance to check the EntityState of specified entity
    var studentEntry = context.Entry(disconnectedStudent);
    var addressEntry = context.Entry(disconnectedStudent.StudentAddress);

    Console.WriteLine("Student: {0}", studentEntry.State);
    Console.WriteLine("StudentAddress: {0}", addressEntry.State);
}

Kết quả khi biên dịch và chạy chương trình:

Student: Unchanged
StudentAddress: Unchanged

Lớp DbEntityEntry

Lớp DbEntityEntry là một lớp quan trọng, hữu ích trong việc truy xuất các thông tin khác nhau về một thực thể. Bạn có thể lấy một thể hiện của DBEntityEntry của một thực thể cụ thể bằng cách sử dụng phương thức Entry của DbContext. Ví dụ:

DbEntityEntry studentEntry = dbcontext.Entry(entity);

Lớp DbEntityEntry cho phép bạn truy cập trạng thái của thực thể và các giá trị hiện tại và ban đầu của tất cả các thuộc tính của một thực thể nhất định.

Đoạn mã ví dụ sau đây cho thấy cách truy xuất thông tin quan trọng của một thực thể cụ thể.

using (var dbCtx = new SchoolDBEntities())
{
    //get student whose StudentId is 1
    var student = dbCtx.Students.Find(1);

    //edit student name
    student.StudentName = "Edited name";

    //get DbEntityEntry object for student entity object
    var entry = dbCtx.Entry(student);

    //get entity information e.g. full name
    Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().FullName);

    //get current EntityState
    Console.WriteLine("Entity State: {0}", entry.State );

    Console.WriteLine("********Property Values********");

    foreach (var propertyName in entry.CurrentValues.PropertyNames )
    {
        Console.WriteLine("Property Name: {0}", propertyName);

        //get original value
        var orgVal = entry.OriginalValues[propertyName];
        Console.WriteLine("     Original Value: {0}", orgVal);
                    
        //get current values
        var curVal = entry.CurrentValues[propertyName];
        Console.WriteLine("     Current Value: {0}", curVal);
    }
}

Đây là kết quả khi biên dịch và thực thi:

Entity Name: Student
Entity State: Modified
********Property Values********
Property Name: StudentID
Original Value: 1
Current Value: 1
Property Name: StudentName
Original Value: First Student Name
Current Value: Edited name
Property Name: StandardId
Original Value:
Current Value:

Lớp  DbEntityEntry cho phép bạn thiết lập trạng thái của EntityState như ví dụ bên dưới.

context.Entry(student).State = System.Data.Entity.EntityState.Modified;

Truy cập MSDN để biết thêm thông tin về lớp DbEntityEntry.

Entity Framework
Bài Viết Liên Quan:
2 kịch bản lưu dữ liệu trong Entity Framework Core
Trung Nguyen 30/04/2020
2 kịch bản lưu dữ liệu trong Entity Framework Core

2 kịch bản lưu dữ liệu trong Entity Framework Core là kịch bản được kết nối và kịch bản ngắt kết nối.

Ứng dụng Entity Framework Core đầu tiên
Trung Nguyen 29/04/2020
Ứng dụng Entity Framework Core đầu tiên

Tạo ứng dụng .NET Core Console đầu tiên và cấu hình sử dụng Entity Framework Core.

Truy vấn trong Entity Framework Core
Trung Nguyen 29/04/2020
Truy vấn trong Entity Framework Core

Truy vấn trong Entity Framework Core có gì mới? Truy vấn trong EF Core khác EF ở những điểm nào.

Entity Framework Core toàn tập
Trung Nguyen 29/04/2020
Entity Framework Core toàn tập

Entity Framework Core toàn tập sẽ hướng dẫn bạn tất cả mọi thứ về Entity Framework Core.