Tổng hợp về Entity Framework

Bài viết này sẽ chia sẻ với bạn một số kiến thức bé bé, xinh xinh được tổng hợp lại trong Entity Framework.

Trình duyệt mô hình

Trình duyệt mô hình (Model Browser) cung cấp cho bạn thông tin về tất cả các đối tượng và chức năng mà Entity Data Model (EDM) đã tạo.

Để mở Trình duyệt mô hình, nhấp chuột phải vào chỗ trống trong trình thiết kế EDM và chọn Model Browser từ menu ngữ cảnh.

Model Browser sẽ xuất hiện ở khu vực bạn thường tìm thấy Solution Explorer và Properties.

Trình duyệt mô hình chứa tất cả thông tin về EDM, mô hình khái niệm, mô hình lưu trữ và thông tin ánh xạ, như được hiển thị bên dưới.

Model Browser trong Entity Framework

Như bạn có thể thấy trong hình trên, Trình duyệt mô hình chứa các đối tượng sau:

Diagrams: Trình duyệt mô hình chứa các sơ đồ trực quan của EDM. Chúng ta đã thấy một sơ đồ trực quan mặc định được tạo bởi EDM. Bạn cũng có thể tạo nhiều sơ đồ cho một EDM.

Entity Types: Chứa danh sách các lớp thực thể ánh xạ tới các bảng trong cơ sở dữ liệu.

Complex Types: Các kiểu dữ liệu phức tạp là các lớp được tạo bởi EDM để chứa kết quả của các stored procedure, các table-valued function, v.v. Các kiểu dữ liệu phức tạp này là các lớp tùy chỉnh cho các mục đích khác nhau.

Enum Types: Chứa các thực thể được sử dụng làm enum trong Entity Framework.

Associations: Chứa tất cả các mối quan hệ khóa ngoại giữa các thực thể.

Function Imports: Chứa tất cả các chức năng sẽ được ánh xạ tới các stored procedure, các table-valued function, v.v ... Các stored procedure và các table-valued function sẽ được sử dụng như các phương thức chứ không phải là các thực thể trong Entity Framework.

.Store: Đại diện cho lược đồ cơ sở dữ liệu (SSDL).

Lớp DbEntityEntry trong Entity Framework

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.

Kiểu dữ liệu không gian trong Entity Framework

MS SQL Server 2008 đã giới thiệu hai kiểu dữ liệu không gian geographygeometry. Kiểu dữ liệu geography mô tả dữ liệu trong một vòng quanh trái đất hệ tọa độ và kiểu dữ liệu geometry mô tả dữ liệu trong một hệ tọa độ Euclidean (phẳng).

Bắt đầu với phiên bản Entity Framework 5.0 đã bổ sung các kiểu dữ liệu đặc biệt trong namespace System.Data.Entity.Spatial: DbGeography cho kiểu dữ liệu geographyDbGeometrycho kiểu dữ liệu geometry trong Máy chủ SQL.

Với mục đích demo, chúng tôi sẽ thêm cột Location có kiểu dữ liệu geography trong bảng Course, như được hiển thị trong hình bên dưới:

Kiểu dữ liệu không gian trong Entity Framework

Bây giờ, chúng ta sẽ cập nhật mô hình dữ liệu thực thể (EDM) sau khi thêm cột Location trong cơ sở dữ liệu. Sau khi cập nhật EDM, bạn có thể thấy rằng thuộc tính Location của thực thể Course có kiểu dữ liệu là System.Data.Spatial.DBGeography, như được trình bày trong ví dụ bên dưới:

public partial class Course
{
    public Course()
    {
        this.Students = new HashSet<Student>();
    }
    
    public int CourseId { get; set; }
    public string CourseName { get; set; }
    public Nullable<int> TeacherId { get; set; }
    public System.Data.Spatial.DbGeography Location { get; set; }
    
    public virtual Teacher Teacher { get; set; }
    public virtual ICollection<Student> Students { get; set; }
}

Bây giờ bạn có thể sử dụng thuộc tính Location trong khi thêm hoặc cập nhật một thực thể Course, như được trình bày trong ví dụ bên dưới.

using (var ctx = new SchoolDBEntities())
{
    ctx.Courses.Add(new Course() { 
                CourseName = "New Course", 
                Location = DbGeography.FromText("POINT(-122.360 47.656)") 
        });

    ctx.SaveChanges();
}

Truy cập MSDN để biết thêm thông tin về kiểu dữ liệu geographykiểu dữ liệu geometry trong MS SQL Server.

View trong Entity Framework

Ở phần này, chúng ta sẽ tìm hiểu cách truy vấn View bằng cách sử dụng phương pháp tiếp cận Database First trong Entity Framework 6.

Bạn có thể sử dụng view giống như cách bạn có thể sử dụng các bảng. Vì vậy, trước hết bạn phải thêm view trong cơ sở dữ liệu vào EDM.

Nó sẽ xem view như một thực thể. Vì vậy, bạn có thể làm việc với nó giống như các thực thể thông thường  ngoại trừ thao tác CUD (Create, Update, Delete).

Chúng tôi đã có sẵn view View_StudentCourse trong cơ sở dữ liệu. Chúng tôi muốn thực thi và đọc dữ liệu từ nó bằng cách sử dụng Entity Framework 6 (EF 6).

Để đọc dữ liệu từ view, trước tiên chúng ta cần thêm nó vào EDM (Entity Data Model - Mô hình dữ liệu thực thể). Bạn có thể thêm view trong khi tạo EDM mới hoặc cập nhật EDM hiện có.

Sau khi đã thêm view vào EDM, bây giờ bạn đã có thể thực thi View_studentCourse bằng cách sử dụng một thể hiện của DbContext, như được trình bày ở ví dụ bên dưới.

using (var context = new SchoolDBEntities())
{
    var studentAndCourseList = context.View_StudentCourse.ToList();
    
    foreach (var item in studentAndCourseList)
    {
        Console.WriteLine($"Student: {item.StudentName} Course: {item.CourseName}"); 
    }
}

Theo cách này, bạn có thể làm việc với view bằng Entity Framework 6.x.

Validate Entity trong Entity Framework

Bạn có thể xác thực dữ liệu của các thực thể trong Entity Framework. Để thực hiện điều này, ghi đè phương thức ValidateEntity của DbContext như dưới đây.

protected override System.Data.Entity.Validation.DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, System.Collections.Generic.IDictionary<object, object> items)
{
    if (entityEntry.Entity is Student)
    {
        if (entityEntry.CurrentValues.GetValue<string>("StudentName") == "")
        {
            var list = new List<System.Data.Entity.Validation.DbValidationError>();
            list.Add(new System.Data.Entity.Validation.DbValidationError("StudentName", "StudentName is required"));

            return new System.Data.Entity.Validation.DbEntityValidationResult(entityEntry, list);
        }
    }
    return base.ValidateEntity(entityEntry, items);
}

Như bạn có thể thấy trong đoạn mã trên, chúng tôi đang xác thực dữ liệu của thực thể Student. Nếu StudentName trống, thì chúng tôi sẽ thêm DBValidationError vào DBEntityValidationResult. Vì vậy, bất cứ khi nào bạn gọi phương thức DbContext.SaveChanges và cố gắng lưu thực thể Student mà không có dữ liệu cho thuộc tính StudentName, thì nó sẽ ném ngoại lệ DbEntityValidationException. Hãy xem xét ví dụ sau.

try
{
    using (var ctx = new SchoolDBEntities())
    {
        ctx.Students.Add(new Student() { StudentName = "" });
        ctx.Standards.Add(new Standard() { StandardName = "" });

        ctx.SaveChanges();
    }
}
catch (DbEntityValidationException dbEx)
{
    foreach (DbEntityValidationResult entityErr in dbEx.EntityValidationErrors)
    {
        foreach (DbValidationError error in entityErr.ValidationErrors)
        {
            Console.WriteLine("Error Property Name {0} : Error Message: {1}",
                                error.PropertyName, error.ErrorMessage);
        }
    }
}
Lưu ý: phần này chỉ muốn trình bày cho bạn biết về xác thực dữ liệu thực thể trong Entity Framework. Chúng tôi khuyên bạn không nên sử dụng cách này, thay vào đó bạn nên xác thực dữ liệu của thực thể ở tầng presentation trước khi gọi bất kỳ chức năng nào để tương tác với database.

Cấu hình dựa trên mã trong Entity Framework

Entity Framework 6 đã giới thiệu cấu hình dựa trên mã. Bây giờ, bạn có thể cấu hình các cài đặt liên quan đến Entity Framework bằng mã đã được cấu hình trước đó trong phần <entityframework> của app.config.

Tuy nhiên, app.config được ưu tiên hơn cấu hình dựa trên mã. Nói cách khác, nếu một tùy chọn cấu hình được đặt trong cả mã và app.config, thì cài đặt trong app.config sẽ được sử dụng.

Hãy xem cách triển khai cấu hình dựa trên mã bằng Entity Framework 6.

Trước hết, bạn cần tạo một lớp mới kế thừa lớp DbConfiguration (System.Data.Entity.DbConfiguration):

public class FE6CodeConfig : DbConfiguration
{
    public FE6CodeConfig()
    {
        //define configuration here
    }
}

Bây giờ, bạn có thể thiết lập thuộc tính codeConfigurationType trong app.config như dưới đây:

<entityFramework codeConfigurationType="EF6DBFirstTutorials.FE6CodeConfig, EF6DBFirstTutorials">
</entityFramework>

Hoặc bạn có thể sử dụng thuộc tính DbConfigurationType trên lớp Context để đặt lớp cấu hình dựa trên mã:

Cấu hình dựa trên mã trong Entity Framework
Lưu ý: Entity Framework (EF) không hỗ trợ có nhiều lớp cấu hình được sử dụng trong cùng một AppDomain. Nếu bạn sử dụng thuộc tính này để thiết lập các lớp cấu hình khác nhau cho hai Context, thì một ngoại lệ sẽ được đưa ra.

Bây giờ, bạn có thể sử dụng các phương thức khác nhau DbConfiguration trong phương thức khởi tạo như dưới đây:

Cấu hình dựa trên mã trong Entity Framework

Chúng ta hãy xem cách áp dụng các cài đặt khác nhau bằng cách sử dụng cấu hình dựa trên mã cũng như app.config.

Cấu hình kết nối mặc định

Sử dụng phương thức SetDefaultConnectionFactory() để cấu hình kết nối mặc định, chẳng hạn như SqlConnectionFactory cho SQL Server, như ví dụ bên dưới.

using System.Data.Entity.Infrastructure;

public class FE6CodeConfig : DbConfiguration
{
    public FE6CodeConfig()
    {
        this.SetDefaultConnectionFactory(new SqlConnectionFactory());
    }
}

Kết nối mặc định được cấu hình trong app.config như dưới đây.

<entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>

Thiết lập cơ sở dữ liệu

Sử dụng phương thức SetProviderServices() để cấu hình cơ sở dữ liệu, như ví dụ bên dưới.

public class FE6CodeConfig : DbConfiguration
{
    public FE6CodeConfig()
    {
        this.SetProviderServices("System.Data.SqlClient", 
                System.Data.Entity.SqlServer.SqlProviderServices.Instance);
    }
}

Cơ sở dữ liệu có thể được cấu hình trong app.config, như ví dụ bên dưới.

<entityFramework>
    <providers>
        <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
</entityFramework>

Thiết lập trình khởi tạo cơ sở dữ liệu

Bạn có thể thiết lập trình khởi tạo cơ sở dữ liệu (chỉ dành cho Code First) bằng cách sử dụng cấu hình dựa trên mã như dưới đây:

public class FE6CodeConfig : DbConfiguration
{
    public FE6CodeConfig()
    {
        this.SetDatabaseInitializer<SchoolDBEntities>(new CustomDBInitializer<SchoolDBEntities>());
    }
}

Điều tương tự như trên có thể được cấu hình trong app.config như ví dụ bên dưới.

<entityFramework>
    <contexts>
        <context type="EF6DBFirstTutorials.SchoolDBEntities, EF6DBFirstTutorials"  >
            <databaseInitializer   type="EF6DBFirstTutorials.CustomDBInitializer , EF6DBFirstTutorials">
            </databaseInitializer>
        </context>
    </contexts>    
</entityFramework>


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.