Lưu dữ liệu trong Entity Framework

Có hai kịch bản lưu dữ liệu (persistence) một thực thể vào cơ sở dữ liệu bằng cách sử dụng Entity Framework: Kịch bản được kết nối và Kịch bản bị ngắt kết nối.

Kịch bản được kết nối

Trong kịch bản được kết nối, cùng một thể hiện của lớp Context (kế thừa từ lớp DbContext) được sử dụng để truy xuất và lưu các thực thể.

Nó theo dõi tất cả các thực thể trong suốt vòng đời của nó. Điều này rất hữu ích trong các ứng dụng windows với cơ sở dữ liệu cục bộ hoặc cơ sở dữ liệu trên cùng một mạng.

Kịch bản được kết nối khi lưu dữ liệu trong Entity Framework

Ưu điểm:

  • Thực hiện nhanh.
  • Context theo dõi tất cả các thực thể và tự động đặt trạng thái thích hợp khi các thay đổi xảy ra với các thực thể.

Nhược điểm:

  • Context vẫn còn tồn tại, vì vậy kết nối với cơ sở dữ liệu vẫn mở.
  • Sử dụng nhiều tài nguyên hơn.

Lưu dữ liệu trong kịch bản được kết nối

Lưu dữ liệu thực thể trong kịch bản được kết nối là một nhiệm vụ khá dễ dàng vì Context sẽ tự động theo dõi các thay đổi xảy ra trên thực thể trong suốt vòng đời của nó.

Ở đây, chúng tôi sẽ sử dụng cùng EDM cho các hoạt động CRUD mà chúng tôi đã tạo trong chương Tạo mô hình dữ liệu thực thể. Một thực thể chứa dữ liệu trong thuộc tính của nó sẽ được chèn, cập nhật hoặc xóa, dựa trên nó EntityState.

Trong kịch bản được kết nối, cùng một thể hiện của DbContext sẽ được sử dụng trong việc truy xuất và lưu các thực thể.

Hình dưới đây minh họa các thao tác CUD (Create, Update, Delete) trong kịch bản được kết nối.

Lưu dữ liệu trong kịch bản được kết nối

Theo hình trên, Entity Framework tạo và thực thi các câu lệnh INSERT, UPDATE và DELETE cho các thực thể có trạng thái EntityStateAdded, Modified hoặc Deleted khi gọi phương thức DbContext.SaveChanges().

Trong kịch bản được kết nối, một thể hiện của DbContext theo dõi tất cả các thực thể và do đó, nó sẽ tự động thiết lập một EntityState phù hợp cho từng thực thể bất cứ khi nào một thực thể được tạo, sửa đổi hoặc xóa.

Thêm mới dữ liệu trong kịch bản được kết nối

Sử dụng phương thức DbSet.Add để thêm một thực thể mới vào một Context (ví dụ DbContext), nó sẽ chèn một bản ghi mới vào cơ sở dữ liệu khi bạn gọi phương thức SaveChanges().

using (var context = new SchoolDBEntities())
{
    var std = new Student()
    {
        FirstName = "Bill",
        LastName = "Gates"
    };
    context.Students.Add(std);

    context.SaveChanges();
}

Trong ví dụ trên, đoạn mã context.Students.Add(std) sẽ thêm một thể hiện mới được tạo của thực thể Student vào một Context. Thể hiện này sau khi được thêm vào Context sẽ có trạng thái EntityStateAdded. Phương thức context.SaveChanges() sẽ tạo và thực thi các câu lệnh INSERT sau vào cơ sở dữ liệu.

exec sp_executesql N'INSERT [dbo].[Students]([FirstName], [LastName])
VALUES (@0, @1)
SELECT [StudentId]
FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()',N
''@0 nvarchar(max) ,@1 nvarchar(max) ',@0=N'Bill',@1=N'Gates'
go

Cập nhật dữ liệu trong kịch bản được kết nối

Trong kịch bản được kết nối, Entity Framework API theo dõi tất cả các thực thể được truy xuất bằng Context.

Vì vậy, khi bạn chỉnh sử dữ liệu của thực thể, Entity Framework sẽ tự động đánh dấu trạng thái EntityState của những thực thể này là  Modified. Những thực thể này sẽ được cập nhật vào cơ sở dữ liệu khi bạn gọi phương thức SaveChanges().

using (var context = new SchoolDBEntities())
{
    var std = context.Students.First<Student>(); 
    std.FirstName = "Steve";
    context.SaveChanges();
}

Trong ví dụ trên, chúng tôi lấy sinh viên đầu tiên từ cơ sở dữ liệu bằng cách sử dụng phương thức context.Students.First<student>().

Ngay sau khi chúng tôi cập nhật giá trị cho thuộc tính FirstName, Context sẽ thiết lập trạng thái EntityState của nó là Modified do sửa đổi được thực hiện trong phạm vi của thể hiện DbContext (Context).

Vì vậy, khi chúng ta gọi phương thức SaveChanges(), nó sẽ tạo và thực thi câu lệnh update sau trong cơ sở dữ liệu.

exec sp_executesql N'UPDATE [dbo].[Students]
SET [FirstName] = @0
WHERE ([StudentId] = @1)',
N'@0 nvarchar(max) ,@1 int',@0=N'Steve',@1=2
Go

Trong một câu lệnh cập nhật, Entity Framework API chỉ cập nhật các thuộc tính có giá trị được sửa đổi, các thuộc tính khác bị bỏ qua. Trong ví dụ trên, chỉ có thuộc tính FirstName được chỉnh sửa, vì vậy một câu lệnh cập nhật chỉ cập nhật cột FirstName.

Xóa dữ liệu trong kịch bản được kết nối

Sử dụng phương thức DbSet.Remove() để xóa một bản ghi trong bảng cơ sở dữ liệu.

using (var context = new SchoolDBEntities())
{
    var std = context.Students.First<Student>();
    context.Students.Remove(std);

    context.SaveChanges();
}

Trong ví dụ trên, phương thức context.Students.Remove(std) đánh dấu đối tượng thực thể std có trạng thái EntityStateDeleted. Do đó, Entity Framework sẽ tạo và thực thi câu lệnh DELETE sau trong cơ sở dữ liệu.

exec sp_executesql N'DELETE [dbo].[Students]
WHERE ([StudentId] = @0)',N'@0 int',@0=1
Go

Qua những ví dụ trên chúng ta thấy việc rất dễ dàng để thêm, cập nhật hoặc xóa dữ liệu trong Entity Framework 6.x trong kịch bản được kết nối.

Kịch bản bị ngắt kết nối

Trong kịch bản bị ngắt kết nối, các thể hiện khác nhau của Context được sử dụng để truy xuất và lưu các thực thể vào cơ sở dữ liệu.

Một thể hiện của Context được hủy và giải phóng bộ nhớ sau khi lấy dữ liệu và một thể hiện mới lại được tạo ra để lưu các thực thể vào cơ sở dữ liệu.

Kịch bản bị ngắt kết nối khi lưu dữ liệu trong Entity Framework

Kịch bản bị ngắt kết nối rất phức tạp vì một thể hiện của Context không theo dõi các thực thể, vì vậy bạn phải đặt trạng thái phù hợp cho từng thực thể trước khi lưu các thực thể sử dụng phương thức SaveChanges().

Trong hình trên, ứng dụng lấy biểu đồ thực thể bằng Context1 và sau đó ứng dụng thực hiện một số thao tác CUD (Tạo, Cập nhật, Xóa) bằng Context2. Context2 không biết thao tác nào đã được thực hiện trên biểu đồ thực thể trong phần này kịch bản.

Điều này rất hữu ích trong các ứng dụng web hoặc ứng dụng có cơ sở dữ liệu từ xa.

Ưu điểm:

  • Sử dụng ít tài nguyên hơn so với kịch bản được kết nối.
  • Không mở kết nối với cơ sở dữ liệu.

Nhược điểm:

  • Cần đặt trạng thái thích hợp cho từng thực thể trước khi lưu.
  • Thực hiện chậm hơn so với kịch bản được kết nối.

Lưu dữ liệu trong kịch bản bị ngắt kết nối

Lưu dữ liệu trong kịch bản bị ngắt kết nối hơi khác một chút so với kịch bản được kết nối.

Trong kịch bản bị ngắt kết nối, một thể hiện của DbContext không biết về các thực thể bị ngắt kết nối vì các thực thể được tạo hoặc sửa đổi ngoài phạm vi của DbContext thể hiện hiện tại.

Vì vậy, bạn cần phải đính kèm các thực thể bị ngắt kết nối với một Context với trạng thái EntityState phù hợp để thực hiện các thao tác INSERT hoặc UPDATE trong cơ sở dữ liệu.

Trong kịch bản bị ngắt kết nối, bạn cần tìm hiểu xem một thực thể là thêm mới hay hiện có và dựa vào đó bạn có thể thiết lập trạng thái EntityState.

Ở đây, nếu giá trị thuộc tính khóa bằng 0 thì chúng tôi sẽ coi đó là một thực thể mới và vì vậy chúng tôi sẽ thiết lập trạng thái là Added.

Nếu giá trị thuộc tính khóa lớn hơn 0, thì có nghĩa là nó là một thực thể hiện có và vì vậy chúng tôi sẽ đặt trạng thái là Modified.

Lưu dữ liệu trong kịch bản bị ngắt kết nối

Thêm mới dữ liệu trong kịch bản bị ngắt kết nối

Ví dụ sau đây cho thấy việc lưu một thực thể bị ngắt kết nối.

// disconnected new entity 
var student = new Student()
{ 
    StudentName = "Bill" 
};

using (var context = new SchoolDBEntities())
{
    context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified;

    context.SaveChanges();
}

Trong ví dụ trên, student là một đối tượng thực thể bị ngắt kết nối và context không biết được trạng thái của nó.

Đoạn mã context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified; sẽ thiết lập trạng thái Added nếu giá trị của thuộc tính khóa StudentId bằng 0, ngược lại nó sẽ thiết lập trạng thái là Modified. Phương thức SaveChanges() sẽ tạo và thực hiện lệnh INSERT sau vào cơ sở dữ liệu.

exec sp_executesql N'INSERT [dbo].[Student]([StudentName], [StandardId])
VALUES (@0, NULL)
SELECT [StudentID] FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity(),@0='Bill'

Cập nhật dữ liệu trong kịch bản bị ngắt kết nối

Tương tự, nếu giá trị của StudentId khác 0, thì nó sẽ thiết lập trạng thái EntityStateModified và do đó khi gọi phương thức SaveChanges() sẽ thực thi lệnh UPDATE.

// disconnected existing entity 
var student = new Student()
{
    StudentId = 1,
    StudentName = "Steve" 
};

using (var context = new SchoolDBEntities())
{
    context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified;

    context.SaveChanges();
}

Trong ví dụ trên, một đối tượng của thực thể Student có thuộc tính khóa StudentId lớn hơn 0, vì vậy nó sẽ được đánh dấu là Modified. Điều này sẽ thực hiện lệnh UPDATE sau trong cơ sở dữ liệu.

exec sp_executesql N'UPDATE [dbo].[Student]
SET [StudentName] = @0
WHERE @@ROWCOUNT > 0 AND [StudentID] = @1'N'@0 varchar(50),@1 int',@0='Steve',@1=1

Xóa dữ liệu trong kịch bản bị ngắt kết nối

Xóa một thực thể bị ngắt kết nối rất dễ dàng. Chỉ cần đặt trạng thái thành Deleted bằng phương thức Entry() như ví dụ dưới đây.

// disconnected entity to be deleted
var student = new Student()
{ 
    StudentId = 1 
};

using (var context = new SchoolDBEntities())
{
    context.Entry(student).State = System.Data.Entity.EntityState.Deleted;    

    context.SaveChanges();
}  

Trong ví dụ trên, một thể hiện của thực thể Student chỉ chứa thuộc tính khóa StudentId. Để xóa một thực thể, nó chỉ yêu cầu một thuộc tính khóa. Câu lệnh  context.Entry(student).State = System.Data.Entity.EntityState.Deleted sẽ gắn một thực thể vào một Context và gán trạng thái của nó thành Deleted. Điều này sẽ thực hiện lệnh DELETE sau trong cơ sở dữ liệu.

exec sp_executesql N'DELETE [dbo].[Students]
WHERE ([StudentId] = @0)',N'@0 int',@0=1
Go


Bài viết liên quan:

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.

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 có gì mới? Truy vấn trong EF Core khác EF ở những điểm nào.