Bonus: Sử dụng EDMX diagram với Entity Framework Code-first

    0

    Khi học lập trình Entity Framework bạn đã biết rằng Entity Framework có ba hướng tiếp cận: Model-first, Database-first và Code-first. Trong đó, code-first là hướng tiếp cận tốt nhất. Code first có thể làm việc theo cả hai chiều: sinh ra cơ sở dữ liệu từ domain class, và tạo ra domain class từ cơ sở dữ liệu.

    Tuy vậy, việc viết code cho hàng chục hoặc hàng trăm domain class với các quan hệ rối rắm giữa chúng khiến bạn mất nhiều thời gian và dễ bị lỗi. Bạn buộc phải tưởng tượng ra quan hệ giữa chúng và viết code cho phù hợp.

    Nếu bạn đã từng làm việc với tiếp cận Model-first sử dụng EDMX diagram, bạn hẳn rất thích sự trực quan và nhanh chóng khi tạo ra các entity class. Đặc biệt nếu bạn có số lượng lớn entity class với các quan hệ phức tạp, EDMX diagram là công cụ rất tuyệt vời để “vẽ” ra sơ đồ class phù hợp.

    Tuy nhiên, nhiều người không thích tiếp cận Model-first vì workflow của cách tiếp cận này giới hạn sự kiểm soát.

    Bài viết này hướng dẫn bạn một cách đơn giản để lợi dụng khả năng sinh code domain class tự động từ EDMX và ghép nó vào workflow của code-first. Qua đó bạn tận dụng được ưu thế của cả hai cách tiếp cận. Giải pháp này dựa trên việc sử dụng một thành phần ít người để ý, EF 6.x DbContext Generator, kết hợp với sơ đồ thiết kế EDMX.

    [signinlocker id=”16252″]

    Các vấn đề với Model-first và Code-first

    Ngoại trừ tiếp cận Database-first rất khác biệt vì bạn phải xuất phát từ một cơ sở dữ liệu có sẵn, hai cách tiếp cận còn lại đều hướng đến xây dựng các entity class trước rồi mới ánh xạ chúng thành cơ sở dữ liệu.

    Các phiên bản Visual studio về sau này hỗ trợ hai cách làm việc với cơ sở dữ liệu sẵn có: EF Designer from databaseCode First from database. Cách thứ nhất tạo ra mô hình entity trên giao diện thiết kế EDMX. Cách thứ hai tạo ra các file code cho entity class. Code First from database lại được xem là thuộc tiếp cận Code First. Tuy nhiên, trong post này chúng ta không xem xét tình huống có sẵn cơ sở dữ liệu.

    Cách tiếp cận Code-first

    Trong cách tiếp cận Code-first, bạn tự mình code các entity class. Entity Framework giúp bạn ánh xạ các entity class sang cơ sở dữ liệu.

    Code-first là cách tiếp cận “toàn năng” nhất. Thậm chí, trong Entity Framework Core chỉ còn lại mỗi cách tiếp cận này. Các tài liệu dạy Entity Framework gần đây tập trung chủ yếu vào hướng tiếp cận này.

    Code-first nhẹ nhàng và an toàn hơn rất nhiều. Code-first cho bạn kiểm soát mọi quá trình, từ việc viết code đến phân bố các class vào các project.

    Một lợi thế cực lớn của Code-first là cơ chế migration. Migration cho phép bạn chủ động khởi tạo / cập nhật cơ sở dữ liệu mà không làm mất dữ liệu hiện có. Cách sử dụng migration cũng cực kỳ đơn giản và tiện lợi.

    Cách tiếp cận Model-first

    Trong cách tiếp cận Model-first, bạn sử dụng một giao diện đồ họa để thiết kế ra mô hình entity. Giao diện thiết kế này có tên gọi là Entity Designer Model XML, gọi tắt luôn là EDMX.

    Từ mô hình entity sẽ sinh ra mã SQL để tạo/cập nhật cơ sở dữ liệu tương ứng, cũng như các file code của từng entity class.

    Việc thiết kế mô hình entity trên EDMX rất trực quan, tiện lợi và nhanh chóng. Trong thời gian ngắn và mất rất ít công sức, bạn có thể thiết kế ra một mô hình entity lớn và phức tạp.

    Vậy tại sao không kết hợp EDMX với Code-first thành một quy trình làm việc như thế này: thiết kế mô hình entity trên giao diện đồ họa => tự động sinh code cho các entity class => tự động sinh lớp context (entity container) => gọi migration để cập nhật cấu trúc cơ sở dữ liệu.

    Như vậy bạn kết hợp được việc thiết kế trực quan với workflow của Code-first. Tận dụng ưu điểm của cả hai.

    Kết hợp EDMX trong workflow của Code-first

    Chúng ta cùng thực hiện một ví dụ. Hãy chuẩn bị các project sau trong solution:

    1. _Designer (kiểu Class library): project này chỉ chứa bản sơ đồ thiết kế entity. Đặt tên với dấu _ ở đầu để tách biệt nó với cách project còn lại vì trong project này không hề có code.
    2. Models (kiểu Class library): nơi chứa các domain class sinh ra tự động.
    3. DataAccess (Kiểu Class Library): nơi chứa lớp Context (Entity Container) giúp truy xuất dữ liệu. Project này cần tham chiếu tới Models.
    4. ConsoleApp (kiểu Console App): chương trình ứng dụng thử nghiệm sử dụng Models và DataAccess. Project này cần tham chiếu tới cả Models và DataAccess.
    Các project trong solution edmx code-first

    Sử dụng ADO.NET Entity Data Model

    Thêm một item mới thuộc loại ADO.NET Entity Data Model vào project _Designer. Đặt tên cho nó là University.

    net entity data model

    Lưu ý ở bước thứ hai bạn cần chọn Empty EF Designer model.

    Visual studio sẽ thêm cho bạn ba file University.edmx, University.Designer.csUniversity.edmx.diagram, đồng thời mở ra giao diện thiết kế.

    Thiết kế entity

    Hãy tự thiết kế một sơ đồ entity đơn giản theo ý mình. Chúng tôi đưa ra sơ đồ thiết kế như sau với vai trò ví dụ:

    Bài viết giả định bạn đã biết qua cách làm việc với giao diện thiết kế edmx cũng như biết cách thiết kế sơ đồ entity trên giao diện này. Cách làm việc này khá đơn giản nếu bạn đã học code-first.

    Đây là một sơ đồ entity khá đơn giản với 5 entity (domain) class: Person (abstract), Student, Teacher, Department và School. Trong đó Student và Teacher cùng kế thừa từ abstract class Person. Department có quan hệ một – nhiều với Teacher. School có quan hệ một – nhiều với Department.

    Khi đang ở giao diện thiết kế (và không chọn bất kỳ entity nào), mở cửa sổ Properties (F4) và chỉnh giá trị của Entity Container Name thành UniversityContext:

    Khi sinh code tự động, đây sẽ là tên của context class.

    Sử dụng EF 6.x DbContext Generator

    Thêm một item thuộc loại EF 6.x DbContext Generator vào project DataAccess.

    Hai file University.ttUniversity.Context.tt sẽ được thêm vào project DataAccess. Nếu bạn mở node References cũng sẽ thấy project này được tham chiếu tới hai file thư viện Entity Framework và EntityFramework.SqlServer.

    Mở Nuget Package Manager để update thư viện Entity Framework này lên bản mới nhất (tại thời điểm hiện tại là 6.3).

    Ngay cả khi bạn sử dụng phiên bản mới nhất Visual Studio 2019, thư viện Entity Framework được sử dụng mặc định khi thêm EF 6.x DbContext Generator lại là 6.2. Vì vậy hãy cập nhật lên phiên bản mới nhất.

    EF 6.x DbContext Generator là một thành phần khá “kín tiếng”. Nếu bạn học Entity Framework code-first, khả năng cao là bạn sẽ không biết và không cần sử dụng tới thành phần này.

    Bản thân EF 6.x DbContext Generator chỉ là hai file template (đuôi *.tt) của T4 Text Template. Nhiệm vụ của EF 6.x DbContext Generator là hỗ trợ sinh ra các entity class và context class (còn gọi là Entity Container trong EDMX) từ sơ đồ entity bạn đã thiết kế ở trên.

    Trong ví dụ trên, University.tt là template để tạo ra các file mã nguồn của entity (domain) class. University.Context.tt là template cho context class.

    Nếu bạn để nguyên hai file *.tt trong project DataAcess, cả entity class và context class sẽ nằm trong cùng project này. Đây có thể không phải là điều bạn mong muốn vì context class và entity class có đặc điểm khác nhau và nên thuộc về hai project khác nhau.

    Bạn hãy kéo file University.tt thả vào project Models và xóa bỏ nó trong DataAccess. Sau đó đổi tên University.tt thành University.Models.tt. Việc đổi tên này chỉ nhằm mục đích dễ phân biệt hơn. Bạn cũng có thể tùy ý đổi tên hai file tt này. Nó không ảnh hưởng gì đến các quá trình khác.

    Điều chỉnh các template của EF 6.x DbContext Generator

    Hai template bạn vừa thêm vào vẫn chưa có tác dụng gì. Lý do là vì các template này cần sử dụng file edmx.

    Điều chỉnh University.Models.tt

    Bạn mở file University.Models.tt và tìm đến dòng thứ 5:

    Vị trí này cho phép bạn đặt đường dẫn đến file edmx. Hãy nhập tương tự trên để trỏ tới file University.edmx trong thư mục _Designer.

    Sau khi lưu lại, University.Models.tt bắt đầu tự động sinh code cho các entity class.

    Điều chỉnh University.Context.tt

    Mở tiếp file University.Context.tt và điều chỉnh để trỏ tới file University.edmx giống như trên. Đừng lưu thay đổi ngay.

    Tìm tiếp tới khoảng dòng thứ 45:

    Bạn thêm một dòng using Models; vào sau khối using.

    Lý do phải thêm dòng này là ở chỗ, khi file tt nằm ở project nào, nó sẽ sinh code trong namespace mặc định của project đó. Với logic đó, file University.Context.tt nằm trong project DataAccess, class UniversityContext nó sinh ra sẽ nằm trong namespace DataAccess. Trong khi file University.tt nằm trong project Models, các entity class nó sinh ra đều nằm trong namespace Models. Bạn bổ sung thêm namespace Models vào file này để có thể sử dụng các entity class.

    Bạn tìm tiếp đến khoảng dòng số 84 và comment toàn bộ code của phương thức OnModelCreating.

    Đây là một bước rất quan trọng. Khi sử dụng EF DbContext Generator, phương thức ghi đè OnModelCreating luôn luôn phát ra ngoại lệ throw new UnintentionalCodeFirstException();. Thực ra Generator làm như vậy chính là để ngăn cản bạn vô tình sử dụng workflow của code-first lẫn vào workflow của Model-first (và Database-first). Tuy nhiên, ở đây chúng ta xác định chỉ tận dụng EDMX để sinh class chứ không đi tiếp theo workflow của Model-first nên bạn có thể comment lệnh này.

    Tuy nhiên, một lợi thế lớn của Code-first là bạn có thể sử dụng fluent API để ghi đè cấu hình cho các entity class. Do đó, chúng ta comment luôn cả phương thức này để tạo nó trong một file khác.

    Cũng để ý rằng, các class do Generator tạo ra đều là partial class. Do vậy, bạn có thể khai báo class này trong một file khác và ghi đè OnModelCreating trong file đó để sử dụng fluent API nếu cần thiết.

    Ngay khi lưu file này, công cụ sẽ bắt đầu tự động sinh code cho lớp UniversityContext.

    Kết quả sinh code cho các entity class và context class như sau:

    Nếu mở các file *.cs vừa được sinh ra bạn sẽ thấy nó chỉ là các file code hoàn toàn bình thường, giống hệt như khi bạn tự code khi dùng Code-first.

    Đến đây bạn đã có đầy đủ các domain class và context class theo yêu cầu của Code-first. Bạn có thể tiếp tục áp dụng các bước còn lại theo quy trình của Code-first. Chúng ta sẽ chỉ trình bày vắn tắt các bước này. Nếu muốn tìm hiểu kỹ hơn, bạn có thể đọc bài viết về quy trình làm việc với Code-first trong khóa học Lập trình Entity Framework.

    Sử dụng code-first

    Nếu bạn mở class UniversityContext sẽ thấy code như sau:

    namespace DataAccess
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Infrastructure;
        using Models;
        
        public partial class UniversityContext : DbContext
        {
            public UniversityContext()
                : base("name=UniversityContext")
            {
            }
        
            /* protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                throw new UnintentionalCodeFirstException();
            } */
        
            public virtual DbSet<Person> People { get; set; }
            public virtual DbSet<Department> Departments { get; set; }
            public virtual DbSet<School> Schools { get; set; }
        }
    }

    Với cách gọi base constructor như trên, bạn cần tạo một connectionString có tên UniversityContext trong file App.Config của DataAccess.

    Bạn có thể tìm hiểu kỹ hơn về các cách truyền tham số cho base constructor trong bài viết về khởi tạo và cập nhật CSDL trong code-first.

    Mở file App.Config của DataAccess và thêm node connectionStrings như sau:

    <connectionStrings>
        <add name="UniversityContext" providerName="System.Data.SqlClient" connectionString="Server=.;Initial Catalog=Univeristy;Integrated Security=true" />
    </connectionStrings>

    Tự điều chỉnh chuỗi trên cho phù hợp với cấu hình máy của bạn.

    Tiếp theo hãy kích hoạt chế độ automatic migration cho DataAccess: enable-migrations –EnableAutomaticMigration:$true trong Package Manager Console.

    Hãy đọc bài học về migration trong code-first nếu bạn chưa biết.

    Cuối cùng chạy lệnh update-database trong Package Manager Console để tạo cơ sở dữ liệu.

    Đến đây xin chúc mừng bạn đã tích hợp thành công edmx với code first sử dụng EF DbContext generator!

    Qua quá trình trên bạn thấy rằng, quá trình thiết kế entity và sử dụng EF DbContext Generator thuộc về tiếp cận Model-first. Quá trình kích hoạt migration trở đi thuộc về tiếp cận Code-first. Model-first giúp bạn tạo ra entity class và context class một cách nhanh chóng để sử dụng tiếp trong workflow của Code-first.

    Hãy thực hiện lại ví dụ trên một vài lần để ghi nhớ các bước thực hiện. Lần đầu tiên bạn có thể thấy hơi rắc rối nhưng từ những lần sau bạn sẽ thấy các bước trên thực sự rất đơn giản.

    Giải quyết một số tình huống thường gặp

    Cập nhật entity model

    Vấn đề đầu tiên mà bạn sẽ gặp là nhu cầu cập nhật entity model. Bạn hãy mở giao diện thiết kế và thực hiện tất cả những thay đổi cần thiết rồi lưu lại.

    Click phải vào file University.Models.tt (project Models) và chọn Run Custom Tool. Thao tác này sẽ tự động sinh lại code cho tất cả các entity class.

    Thực hiện tương tự với University.Context.tt để sinh lại code cho context class.

    Sau khi thay đổi code, bạn chạy lệnh update-database của code-first migration để cập nhật cấu trúc cơ sở dữ liệu.

    Thêm calculated property cho entity class

    Trong nhiều tình huống bạn muốn thêm một số property cho entity class mà giá trị của những property này được tính theo giá trị của các property khác. Loại property như vậy thường gọi là calculated property hoặc read-only property. Bạn sẽ không muốn lưu những property này vào CSDL.

    Lấy ví dụ, trong class Person ở trên bạn có FirstName, LastName. Giờ bạn muốn tạo property FullName như sau:

    public string FullName => $"{FirstName} {LastName}";

    Bạn có cần lưu FullName vào CSDL không? Có lẽ là không, và do đó không nên điều chỉnh entity model.

    Giờ hãy thêm một file code PersonExt.cs vào project Models.

    Lưu ý, đừng đặt tên file code là Person.cs, vì nó sẽ ghi đè lên file code Generator sinh tự động.

    Trong file code này định nghĩa một partial class Person như sau:

    namespace Models
    {
        public partial class Person
        {
            public string FullName => $"{FirstName} {LastName}";
        }
    }

    Thế là xong. Vấn đề là, các class do Generator sinh tự động đều là partial class. Person, Teacher, Student, Department và School đều là các partial class. Do đó, bạn cứ tạo partial class tương ứng trong file khác và đưa điều chỉnh của mình vào đó.

    Lưu ý: tuyệt đối không sửa mã nguồn do Generator sinh tự động. Mỗi lần cập nhật entity từ designer, các file này sẽ được sinh code mới (xóa hết code cũ).

    Sử dụng Fluent API

    Do các entity class được sinh tự động, phương thức cấu hình tự nhiên nhất là tuân thủ theo các quy ước (convention) của code-first. Bạn có lẽ cũng biết, convention không thực sự đủ để cấu hình CSDL từ code-first. Do vậy, Code-first mới cung cấp thêm khả năng cấu hình sử dụng annotation attribute hoặc fluent API.

    Do entity class sinh tự động, bạn không sử dụng được annotation attribute. Do vậy, phương án cấu hình khả thi nhất là fluent API.

    Tạo file UniversityContext.cs trong DataAccess và viết code như sau:

    using System.Data.Entity;
    using Models;
    namespace DataAccess
    {
        partial class UniversityContext
        {
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Person>()
                    .Property(p => p.FirstName)
                    .HasMaxLength(40)
                    .IsRequired();
            }
        }
    }

    Để sử dụng fluent API, bạn phải ghi đè phương thức OnModelCreating trong context class. Trong ví dụ trên, context class là UniversityContext, và bản thân nó cũng là một partial class. Do vậy, bạn có thể tạo ra một file khác cho class này và ghi đè OnModelCreating trong file mới.

    Hãy nhớ lại khi chỉnh sửa file University.Context.tt, bạn comment hết cả phương thức OnModelCreating. Đây chính là cách để chúng ta ghi đè phương thức này trong file mới do bạn tự viết. Nếu không comment như vậy, bạn không thể ghi đè OnModelCreating trong file mới được (ghi đè chỉ được viết một lần trong class).

    Kết luận

    Bài viết này giúp bạn làm quen với một giải pháp kết hợp khả năng thiết kế của Model-first với workflow của Code-first. Sự kết hợp này có thể rất hữu ích nếu bạn phải tạo ra các domain class phức tạp.

    Nếu chưa tự thực hiện lại được ví dụ trên hoặc có điều gì chưa rõ, bạn có thể tải mã nguồn solution để tham khảo.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    [/signinlocker]

    Theo dõi
    Thông báo của
    guest

    0 Thảo luận
    Phản hồi nội tuyến
    Xem tất cả bình luận