Thiết kế Entity Data Model – Part 3: Code First

Code First Sample Publishing DatabaseBài này giới thiệu cách sử dụng EF 4.1 để thiết kế Entity Data Model theo hướng Code First. Sử dụng Code First, bạn phải làm thủ công toàn bộ việc định nghĩa các class của object layer. Nhờ đó bạn sẽ hiểu sâu hơn cách hoạt động và cấu trúc của các class này.

ADO.NET Entity Framework 4.2

Phiên bản EF 4.1 cung cấp hai tính năng mới:

  • The DbContext API is a simplified abstraction over ObjectContext and a number of other types that were included in previous releases of the ADO.NET Entity Framework. The DbContext API surface is optimized for common tasks and coding patterns.
  • Code First is a new development pattern for the ADO.NET Entity Framework and provides an alternative to the existing Database First and Model First patterns. Code First is focused around defining your model using C#/Visual Basic .NET classes, these classes can then be mapped to an existing database or be used to generate a database schema. Additional configuration can be supplied using Data Annotations or via a fluent API.

Phiên bản 4.2 vừa được phát hành đầu tháng 11/2011 và  sửa một số lỗi của phiên bản EF 4.1. Để cài đặt EF 4.2, bạn cần dùng đến công cụ Package Manager Console trong Visual Studio. Công cụ được cung cấp trong menu Tools> Library Package Manager>Package Manager Console.

Khi cửa sổ này được mở ra, tại dấu nhắc dòng lệnh PM>, hãy gõ vào “Install-Package EntityFramework”, hãy chờ một lúc để chương trình download và cài đặt EF 4.2.

Package Manager Console - Installing Entity Framework 4.2

Nếu cần gỡ bỏ, bạn có thể gõ lệnh “Uninstall-Package EntityFramework”.

Tạo các entity class

Để sử dụng Code First trong Entity Framework, bạn cần phải thêm tham chiếu đến các assembly cần thiết vào dự án:

  • EntityFramework
  • System.ComponentModel.DataAnnotations
  • System.Data.Entity

Với phiên bản EF 4.1, bạn có thể thêm các tham chiếu thủ công. Tuy nhiên với EF 4.2, bạn có thể làm điều này bằng cách cài đặt package EntityFramework.Preview. Bạn thực hiện điều này bằng cách làm theo các bước sau:

–          Tạo một dự án với tên EntityFrameworkDemo.

–          Vào menu Project>Add Library Package Reference.

–          Trong cửa sổ hiện ra, qua thẻ Online và gõ vào textbox tìm kiếm tên của package là “EntityFramework.Preview”.

–          Sau khi tìm được, nhấn Install để tiến hành cài đặt.

Install EntityFramework.Preview Package

Sau đó, bạn có thể tạo các class để thực hiện thao tác với database. Trong ví dụ này tôi sẽ tạo các class cho database như sau:

Code First Sample Publishing Database

Các class của object layer, bao gồm một context và các entity được định nghĩa trong C# như sau:

public class CompanyContext : DbContext
{
    public DbSet<Author> Authors { get; set; }
    public DbSet<Article> Articles { get; set; }
    public DbSet<Payroll> Payrolls { get; set; }

}
public class Author
{
    public int AuthorId { get; set; }
    public string Name { get; set; }

    public ICollection<Article> Articles { get; set; }
}

public class Article
{
    public int ArticleId { get; set; }
    public string Name { get; set; }
    public int AuthorId { get; set; }

    public Author Author { get; set; }
}
public class Payroll
{
    public int PayrollId { get; set; }
    public int AuthorId { get; set; }
    public int Salary { get; set; }

    public Author Author { get; set; }
}

Bổ sung thông tin cho các entity class với Data Annotation

Các field của một table cần phải được xác định các thông tin như kiểu dữ liệu, khóa chính, … Tôi làm điều này bằng cách dùng các attribute trong System.ComponentModel.DataAnnotations.

Ví dụ trong class Author, tôi xác định AuthorId là khóa chính bằng attribute [Key], và xác định số kí tự tối đa cho Name là 100 bằng attribute [StringLength].

public class Author
{
    [Key]
    public int AuthorId { get; set; }
    [StringLength(100)]
    public string Name { get; set; }

    public ICollection<Article> Articles { get; set; }
}

Danh sách các annotation mà EF hỗ trợ bao gồm:

  • KeyAttribute
  • StringLengthAttribute
  • MaxLengthAttribute
  • ConcurrencyCheckAttribute
  • RequiredAttribute
  • TimestampAttribute
  • ComplexTypeAttribute
  • ColumnAttribute
    Placed on a property to specify the column name, ordinal & data type
  • TableAttribute
    Placed on a class to specify the table name and schema
  • InversePropertyAttribute
    Placed on a navigation property to specify the property that represents the other end of a relationship
  • ForeignKeyAttribute
    Placed on a navigation property to specify the property that represents the foreign key of the relationship
  • DatabaseGeneratedAttribute
    Placed on a property to specify how the database generates a value for the property (Identity, Computed or None)
  • NotMappedAttribute
    Placed on a property or class to exclude it from the database

Bạn có thể tham khảo hướng dẫn về các annotation này tại Data Annotations in the Entity Framework and Code First.

Kiểm tra kết quả

Để kiểm tra các class đã tạo trên, tôi thực hiện chèn hai dòng dữ liệu vào hai table Authors và Articles. Các table này tương ứng là hai property Authors và Articles của đối tượng CompanyContext. Sau khi đã thực hiện chèn dữ liệu vào, tôi sẽ thử sử dụng một navigation property là Article.Author để xem nó có trả về đúng kết quả không.

static void Main(string[] args)
{
    using (var db = new CompanyContext())
    {
        var author = new Author { Name = "Yin Yang" };
        var article = new Article { Name = "Code First and Entity Framework 4.2"};

        article.AuthorId = author.AuthorId;

        db.Authors.Add(author);
        db.Articles.Add(article);

        db.SaveChanges();
        var au = article.Author;

        Console.WriteLine("Author: "+ au.AuthorId + " - " + au.Name);
    }
    Console.ReadKey();
}

Kết quả:

Author: 1 - Yin Yang

Như bạn thấy tôi hoàn toàn không gán giá trị cho property article.Author, nhưng property này article hoàn toàn chứa tham chiếu đến đối tượng author mà tôi đã chèn vào database, và do đó, in ra được thông tin của author.

Mở Sql Server lên, bạn có thể thấy một database mới tự động được tạo với tên EntityFrameworkDemo.CompanyContext, đây chính là tên đầy đủ của class context mà tôi tạo trong phần trên.

Xác định database sẽ kết nối với connection string

Class DbContext cho phép truyền một  chuỗi kết nối vào constructor. Vì vậy nếu như đã có sẵn một database hoặc muốn đặt tên cho database sẽ được tạo ra, bạn có thể xác định bằng cách truyền chuỗi kết nối cho DbContext. Ví dụ tôi muốn database sẽ được tạo ra phải tên là “Y2Company”, tôi thực hiện bằng cách thêm một chuỗi kết nối với tên “MyCompany” vào tập tin App.Config.

<connectionStrings>
  <add name="MyCompany"
    providerName="System.Data.SqlClient"
    connectionString="Server=.\SQLEXPRESS;Database=Y2Company;Trusted_Connection=true;"/>
<!--...-->
</connectionStrings>

Thêm constructor sử dụng tham số kiểu chuỗi cho class CompanyContext:

public class CompanyContext : DbContext
{
    public DbSet<Author> Authors { get; set; }
    public DbSet<Article> Articles { get; set; }
    public DbSet<Payroll> Payrolls { get; set; }

    public CompanyContext()
    { }
    public CompanyContext(string connectionString)
        :base(connectionString)
    { }
}

Và sửa lại một chút trong phương thức Main(), chú ý phần khởi tạo đối tượng CompanyContext:

var db = new CompanyContext("name=MyCompany");

https://yinyangit.wordpress.com

Bài liên quan:

Update:

Sửa ảnh mô hình database theo code. (cảm ơn bạn Thanh)

19 bình luận về “Thiết kế Entity Data Model – Part 3: Code First

  1. Mình cũng ko có nhiều kinh nghiệm làm những vấn đề này trong thực tế, nhưng có lẽ điều này tùy thuộc vào mục đích và …ý thích của bạn. Nhiều lập trình viên thích CodeFirst vì nó giúp hiểu rõ cơ chế và cấu trúc của model, tuy nhiên lại mất thời gian và phức tạp. Nhiều người thì thích ModelFirst vì nó trực quan, đơn giản và không mất nhiều thơi gian. Theo mình đọc trên mạng thì cả hai cách này đều được sử dụng nhiều, có lẽ còn tùy thuộc vào độ phức tạp của database và yêu cầu của dự án.

  2. Chào anh trong class Author cùa anh có đoạn
    public ICollection Articles { get; set; }
    Cho em hỏi là tại sao anh lại dùng một interface là ICollection. Đoạn trên thì có người dùng interface nhưng là dùng IList thay vì List. Dùng 1 interface là có ý gì nhỉ.

  3. Khi dùng interface thì sẽ linh động hơn do đặc tính đa hình (polymorphism) của hướng đối tượng. Với interface ICollection bạn có thể gán cho property Aritcles này các đối tượng có kiểu được hiện thực (trực tiếp hoặc gián tiếp) interface này.

  4. CHào anh . Em mới theo học code first
    Như bạn TrầnNgocQuoc đã hoi
    “Em thấy trong CSDL của anh bảng Author cúa ID firstame lastname mà anh tạo class chỉ có Id với name thôi.rồi khi lưu vào CSDL sao lưu”
    Anh có thể giải thích rõ hơn không .
    – Tại sao chỉ có AuthorID vơi name ma ko phai là ID, firtname, lastname trên class
    public class Author
    {
    public int AuthorId { get; set; }
    public string Name { get; set; }
    public ICollection Articles { get; set; }
    }

    Nếu làm như A thì khi chạy database sinh ra có giống như mấy Table trên không ?

    • Xin lỗi các bạn và cảm ơn bạn Thanh đã nêu thắc mắc giúp mình nhận thấy hình ảnh có điểm sai so với code. Do lúc code có thay đổi các property khác với ý định ban đầu mà quên sửa lại ảnh khiến người đọc cảm thấy khó hiểu.
      Mình xin trả lời bạn như sau:
      – Nếu database chưa có thì khi chương trình chạy nó sẽ tạo một database và các table như trong định nghĩa của các class entity.
      – Nếu database đã tồn tại và khác với các class entity thì trong Entity Framework 4.3 trở lên, nó sẽ ném một exception và khuyến cáo bạn dùng Code First Migrations để thực hiện việc cập nhật thay đổi.

  5. Pingback: WPF: Sử dụng Entity framework Code first với CSDL SQL Server có sẵn | Blog của Lê Văn Luật

  6. Pingback: WPF: Sử dụng Entity framework Code first với CSDL SQL Server có sẵn | vuongquyen

  7. Anh ơi cho em hỏi là em có 1 bảng Customer trong database, em muốn update thông tin riêng và update password riêng. nhưng mà em xài validate bằng DataAnnotations thì khi mà em muốn update thông tin(Tên, địa chỉ, số điện thoại) nó hok cho update tại vì password trống, em muốn hỏi anh có thuộc tính nào để mà em chỉ update cái thông tin(Tên, địa chỉ, số điện thoại) trong database của Customer thôi mà giữ lại hết password của ng đó. Hiện tại thì em cũng chỉ nghĩ ra là dùng @Html.Hidded cho cái password rồi dùng Json lấy lại giá trị của cái password thông qua id rồi đưa giá trị vào cho nó thôi. Mong anh giúp dùm em, Thanks anh nhiều lắm!

  8. Chào anh.
    Hiện tại em đang tìm hiểu về ADO.Net Entity Framework.
    Em thực hiện xây dựng 1 ứng dụng nhỏ sử dụng EF code first với database có trước. điều đáng nói là tốc độ thực thi của các câu lệnh ở lần đầu thực hiện rất chậm. ( 1 câu select dữ liệu mất tới 3s cho 2 dòng dữ liệu). Từ lần thứ 2 thực hiện thì lại nhanh.
    Em không biết là do mình làm sai ở bước nào. hay bản thân cái EF nó như thế?
    Em thấy trong lập trình web MVC 4 -5 các video hứong dẫn đa số người ta thường dùng EF để thực hiện. phải chăng khi đặt ở sever thì chương trình đã biuld sẵn lần đầu nên ở các lần tiếp theo khi client yêu cầu xử lý dữ liệu thì không cần bận tâm về tốc độ nữa.?
    Anh cho em biết EF thích hợp với việc lập trình website hơn hay application hơn? Project nhỏ hay lớn? Và cách khắc phục tốc độ ở lần đầu build chương trình nữa?

  9. Bài viết hay (y). Mình cũng có cùng thắc mắc với bạn tuongpv, EF rất chậm với những bảng nào có nhiều hàng, thực hiện nhiều truy vấn. Cụ thể mình thêm (Add()) 100k hàng mà mất gần 4 tiếng đồng hồ. dùng phương thức Load() thì mất những 200MB RAM :(( hay tại mình chưa làm đúng cách ạ?

  10. Tại sao authorID trong code của anh lại có giá trị bằng 1 vậy ạ. Em thấy trong hàm main của anh không có chỗ nào set giá trị cho AuthorID hết. và cả trong phần khởi tạo database cũng có vẻ như không có code để phát sinh giá trị cho AuthorID

Đã đóng bình luận.