I. Thêm Entity Graph sử dụng DbContext

Adding entity graph with all new entities is a simple task. We can use DbSet.Add() method to attach a whole entity graph to the context and set all the entity’s states to Added as we have seen in the previous section.

Thêm entity graph với tất cả những thực thể mới là một tác vụ đơn giản. Chúng ta có thể sử dụng phương thức DbSet.Add() để gắn toàn bộ entity graph vào context và cài đặt trạng thái của thực thể thành Added như chúng ta đã nhìn thấy trong phần trước.

Ví dụ sau thêm một entity graph sinh viên trong mô hình disconnected sử dụng phương thức DbSet.Add() và nó đánh dấu trạng thái Added tới tất cả những thực thể:

//Create student in disconnected mode
Student newStudent = new Student() { StudentName = "New Single Student" };
            
//Assign new standard to student entity
newStudent.Standard = new Standard() { StandardName = "New Standard" };
            
//add new course with new teacher into student.courses
newStudent.Courses.Add(new Course() { CourseName = "New Course for single student", Teacher = new Teacher() { TeacherName = "New Teacher" } });

using (var context = new SchoolDBEntities())
{
    context.Students.Add(newStudent);
    context.SaveChanges();

    Console.WriteLine("New Student Entity has been added with new StudentId= " + newStudent.StudentID.ToString());
    Console.WriteLine("New Standard Entity has been added with new StandardId= " + newStudent.StandardId.ToString());
    Console.WriteLine("New Course Entity has been added with new CourseId= " + newStudent.Courses.ElementAt(0).CourseId.ToString());
    Console.WriteLine("New Teacher Entity has been added with new TeacherId= " + newStudent.Courses.ElementAt(0).TeacherId.ToString());
}
Output:

New Student Entity has been added with new StudentId= 14
New Standard Entity has been added with new StandardId= 6
New Course Entity has been added with new CourseId= 7
New Teacher Entity has been added with new TeacherId= 9

Và thực thi những câu lệnh sau tới CSDL:

exec sp_executesql N'INSERT [dbo].[Standard]([StandardName], [Description])
VALUES (@0, NULL)
SELECT [StandardId]
FROM [dbo].[Standard]
WHERE @@ROWCOUNT > 0 AND [StandardId] = scope_identity()',N'@0 varchar(50)',@0='New Standard'
go

exec sp_executesql N'INSERT [dbo].[Student]([StudentName], [StandardId])
VALUES (@0, @1)
SELECT [StudentID]
FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity()',N'@0 varchar(50),@1 int',@0='New Single Student',@1=61
go

exec sp_executesql N'INSERT [dbo].[Teacher]([TeacherName], [StandardId])
VALUES (@0, NULL)
SELECT [TeacherId]
FROM [dbo].[Teacher]
WHERE @@ROWCOUNT > 0 AND [TeacherId] = scope_identity()',N'@0 varchar(50)',@0='New Teacher'
go

exec sp_executesql N'INSERT [dbo].[Course]([CourseName], [Location], [TeacherId])
VALUES (@0, NULL, @1)
SELECT [CourseId]
FROM [dbo].[Course]
WHERE @@ROWCOUNT > 0 AND [CourseId] = scope_identity()',N'@0 varchar(50),@1 int',@0='New Course for single student',@1=93
go

exec sp_executesql N'INSERT [dbo].[StudentCourse]([StudentId], [CourseId])
VALUES (@0, @1)
',N'@0 int,@1 int',@0=43,@1=72
go

Như vậy không có sự khác biệt khi bạn thêm một thực thể đơn hoặc entity graph trong kịch bản disconnected hoặc connected. Chắc chắn rằng tất cả các thực thể đều mới.

II. Cập nhật Entity Graph sử dụng DbContext

Cập nhật một entity graph trong disconnected scenario là một tác vụ phức tạp. Nó là dễ dàng để thêm một entity graph mới trong mô hình disconnected nhưng để cập nhật một entity graph cần cân nhắc thiết kế cẩn thận.

Vấn đề trong việc cập nhật một entity graph in the disconnected scenario là contex không biết thao tác gì được thực hiện trên nó ở phía client. Như hình minh họa sau context mới không biết trạng thái của mỗi thực thể:

Entity Framework 5.0 Tutorial

Chúng ta cần xác định trạng thái của mỗi thực thể trong entity graph trước khi gọi phương thức SaveChages() của context. Có những kiểu mẫu khác nhau để xác định trạng thái của thực mà chúng ta cần cân nhắc trong khi thiết kết lớp dữ liệu với Entity Framework.

Những kiểu mẫu của việc xác định trạng thái thực thể trong disconnected scenario:

Có vài cách (trình bày bên dưới) để xác định một trạng thái thực thể trong disconnected scenario:

  1. Sử dụng thuộc tính PrimaryKey (Id) của một thực thể.
  2. Có thuộc tính State trong tập thực thể.

Note: Bạn có thể tạo thiết kế riêng của bạn để xác định một trạng thái thực thể dựa trên cấu trúc của bạn. Điều này chỉ dành cho mục đích học hỏi.

1) Sử dụng thuộc tính PrimaryKey của một thực thể:

Bạn có thể sử dụng thuộc tính PrimaryKey (Id) của mỗi thực thể để xác định trạng thái thực thể của nó. Tuy nhiên bạn phải quyết định sử dụng quy tắc cấu trúc nào sau đây:

  • Mỗi kiểu thực thể phải có thuộc tính Id (PK).
  • Giá trị mặc định của thuộc tính Id nên bằng 0.
Entity Framework 5.0 Tutorial

Như bạn thấy trong hình minh họa trên đây, client tìm nạp  entity graph Standard và Teacher và thực hiện vài thao tác trên đó rồi sau đó gửi nó tới Context 2 để lưu lại những thay đổi.

Trong disconnected scenario, context 2 không biết trạng thái của mỗi thực thể. Nó phải xác định trạng thái của thực thể  Standard bằng cách sử dụng StandardId và trạng thái của thực thể Teacher bằng cách sử dụng thuộc tính TeacherId. Nếu giá trị của StandardId và TeacherID là 0 nó có nghĩa là một thực thể mới và nếu nó không bằng 0 thì nó có nghĩa là một thực thể đã chỉnh sửa.

Trong hình minh họa trên Standard, Teacher 1, và Teacher 2 có một  thuộc tính Id không bằng 0 vì vậy chúng được đánh dấu là Modified và Teacher 3 bằng 0 được đánh dấu là Added.

Standard disconnectedStandard = null;

using (var context = new SchoolDBEntities())
{
    context.Configuration.ProxyCreationEnabled = false;

    disconnectedStandard = context.Standards.Where(s => s.StandardId == 58).Include(s => s.Teachers).FirstOrDefault<Standard>();
}
//Update Standard in disconnected mode
disconnectedStandard.StandardName = "Edited Standard Name";
            
//Update teachers collection by editing first teacher and adding new teacher
disconnectedStandard.Teachers.ElementAt(0).TeacherName = "Edited Teacher Name";
disconnectedStandard.Teachers.Add(new Teacher() { TeacherName = "New Teacher", StandardId = disconnectedStandard.StandardId });

using (var newContext = new SchoolDBEntities())
{
    //mark standard based on StandardId
    newContext.Entry(disconnectedStandard).State = disconnectedStandard.StandardId == 0 ? EntityState.Added : EntityState.Modified;

    //mark teacher based on StandardId
    foreach (Teacher tchr in disconnectedStandard.Teachers)
        newContext.Entry(tchr).State = tchr.TeacherId == 0 ? EntityState.Added : EntityState.Modified;
                
                
    newContext.SaveChanges();
}

Thuận lợi:

  • Không cần viết mã hoặc xử lý thêm để xác định trạng thái thực thể.
  • Hiệu năng tốt.

Bất lợi:

  • Mỗi kiểu thực thể cần có một thuộc tính Id. Nó không thể xác định trạng thái của một thực thể không có thuộc tính Id.
  • Không thể định danh một thực thể Unchanged. Nó được cài đặt thành trạng thái Modified thậm chí nếu thực thể không có bất kỳ thay đổi nào. Vì vậy có một câu lệnh update CSDL không cần thiết cho một thực thể unchanged.
  • Không thể kiểm soát được kịch bản xóa thực thể. Nó cần kiểm soát sự xóa bỏ riêng.

2) Có thuộc tính State trong mỗi thực thể:

Một cách khác để xác định trạng thái thực thể là có một thuộc tính State trong mỗi kiểu thực thể và cài đặt một trạng thái thích hợp từ phía client trong mô hình disconnected. Sau đó chúng ta chỉ cần cài đặt trạng thái thực thể như thuộc tính trạng thái của một đối tượng thực thể trước khi gọi SaveChanges của context mới.

VD:

Đầu tiên tạo một giao diện IEntityState với thuộc tính enum gọi là ObjectState:

interface IEntityObjectState
{
    EntityObjectState ObjectState { get; set; }
}

public enum EntityObjectState
{ 
    Added,
    Modified,
    Deleted,
    Unchanged
}

Giờ cài đặt một IEntityObjectState trong mỗi lớp thực thể. Vd: thực thể Standard và Teacher như bên dưới:

public partial class Standard:IEntityObjectState
{
    public Standard()
    {
        this.Students = new HashSet<Student>();
        this.Teachers = new HashSet<Teacher>();
    }
    
    public int StandardId { get; set; }
    public string StandardName { get; set; }
    public string Description { get; set; }
    
    public virtual ICollection<Student> Students { get; set; }
    public virtual ICollection<Teacher> Teachers { get; set; }
    [NotMapped]
    public EntityObjectState ObjectState
    {
        get;
        set;
    }
}

public partial class Teacher:IEntityObjectState
{
    public Teacher()
    {
        this.Courses = new HashSet<Course>();
    }
    
    public int TeacherId { get; set; }
    public string TeacherName { get; set; }
    public Nullable<int> StandardId { get; set; }
    
    public virtual ICollection<Course> Courses { get; set; }
    public virtual Standard Standard { get; set; }

    [NotMapped]
    public EntityObjectState ObjectState
    {
        get;
        set;
    }
}

Cài đặt trạng thái thích hợp trong mô hình disconnected từ phía client:

Teacher existingTeacher = null;

using (var context = new SchoolDBEntities())
{
    context.Configuration.ProxyCreationEnabled = false;
    existingTeacher = context.Teachers.FirstOrDefault<Teacher>();

}
Standard disconnectedStandard = new Standard() { StandardName = "New Standard", ObjectState = EntityObjectState.Added };
existingTeacher.ObjectState = EntityObjectState.Modified;
//add existing teacher(in db) to standard
disconnectedStandard.Teachers.Add(existingTeacher);
//add new standard
disconnectedStandard.Teachers.Add(new Teacher() { TeacherName = "New teacher", StandardId = disconnectedStandard.StandardId, ObjectState = EntityObjectState.Added });

Cài đặt trạng thái thực thể theo ObjectState trước khi gọi.

using (var newContext = new SchoolDBEntities())
{
    //check the ObjectState property and mark appropriate EntityState 
    if (disconnectedStandard.ObjectState == EntityObjectState.Added)
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Added;
    else if (disconnectedStandard.ObjectState == EntityObjectState.Modified)
        newContext.Entry(disconnectedStandard).State =System.Data.Entity.EntityState.Modified;
    else if (disconnectedStandard.ObjectState == EntityObjectState.Deleted)
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Deleted;
    else
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Unchanged;

    //check the ObjectState property of each teacher and mark appropriate EntityState 
    foreach (Teacher tchr in disconnectedStandard.Teachers)
    {
        if (tchr.ObjectState == EntityObjectState.Added)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Added;
        else if (tchr.ObjectState == EntityObjectState.Modified)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Modified;
        else if (tchr.ObjectState == EntityObjectState.Deleted)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Deleted;
        else
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Unchanged;
    }
    //save changes
    newContext.SaveChanges();
}

Thuận lợi:

  • Không cần viết mã hoặc xử lý thêm để xác định trạng thái thực thể.
  • Kiểm soát được thuộc tính trạng thái Added, Modified, Deleted và Unchanged.
  • Không cần gọi cập nhật không cần thiết cho những thực thể unchanged.

Bất lợi:

  • Cần cài đặt những trạng thái thích hợp của mỗi thực thể trong mô hình disconnected. Vì vậy cần đặc biệt cẩn thận trong mô hình disconnected.

Nguồn: http://www.entityframeworktutorial.net/

Advertisements