Khung thực thể - Hướng dẫn nhanh

Khung thực thể là gì?

Entity Framework được phát hành lần đầu tiên vào năm 2008, là phương tiện chính của Microsoft để tương tác giữa các ứng dụng .NET và cơ sở dữ liệu quan hệ. Entity Framework là Object Relational Mapper (ORM) là một loại công cụ giúp đơn giản hóa việc ánh xạ giữa các đối tượng trong phần mềm của bạn tới các bảng và cột của cơ sở dữ liệu quan hệ.

  • Entity Framework (EF) là một khung ORM mã nguồn mở cho ADO.NET là một phần của .NET Framework.

  • ORM đảm nhận việc tạo các kết nối cơ sở dữ liệu và thực thi các lệnh, cũng như lấy các kết quả truy vấn và tự động hiện thực hóa các kết quả đó thành các đối tượng ứng dụng của bạn.

  • ORM cũng giúp theo dõi các thay đổi đối với các đối tượng đó và khi được hướng dẫn, nó cũng sẽ lưu giữ các thay đổi đó trở lại cơ sở dữ liệu cho bạn.

Tại sao Entity Framework?

Entity Framework là một ORM và ORM nhằm mục đích tăng năng suất của nhà phát triển bằng cách giảm tác vụ dư thừa trong việc duy trì dữ liệu được sử dụng trong các ứng dụng.

  • Entity Framework có thể tạo ra các lệnh cơ sở dữ liệu cần thiết để đọc hoặc ghi dữ liệu trong cơ sở dữ liệu và thực thi chúng cho bạn.

  • Nếu đang truy vấn, bạn có thể thể hiện các truy vấn của mình đối với các đối tượng miền của mình bằng cách sử dụng LINQ cho các thực thể.

  • Entity Framework sẽ thực thi truy vấn có liên quan trong cơ sở dữ liệu và sau đó cụ thể hóa kết quả thành các phiên bản của đối tượng miền của bạn để bạn làm việc trong ứng dụng của mình.

Có các ORM khác trên thị trường như NHibernate và LLBLGen Pro. Hầu hết các ORM thường ánh xạ các loại miền trực tiếp với lược đồ cơ sở dữ liệu.

Entity Framework có một lớp ánh xạ chi tiết hơn để bạn có thể tùy chỉnh các ánh xạ, ví dụ: bằng cách ánh xạ một thực thể tới nhiều bảng cơ sở dữ liệu hoặc thậm chí nhiều thực thể vào một bảng.

  • Entity Framework là công nghệ truy cập dữ liệu được khuyến nghị của Microsoft cho các ứng dụng mới.

  • ADO.NET dường như đề cập trực tiếp đến công nghệ cho tập dữ liệu và bảng dữ liệu.

  • Khung thực thể là nơi tất cả các khoản đầu tư trong tương lai đang được thực hiện, điều này đã xảy ra trong một số năm rồi.

  • Microsoft khuyến nghị bạn sử dụng Entity Framework qua ADO.NET hoặc LINQ to SQL cho tất cả các bước phát triển mới.

Mô hình khái niệm

Đối với các nhà phát triển đã quen với việc phát triển tập trung vào cơ sở dữ liệu, sự thay đổi lớn nhất với Entity Framework là nó cho phép bạn tập trung vào miền doanh nghiệp của mình. Bạn muốn ứng dụng của mình làm gì mà không bị giới hạn bởi những gì cơ sở dữ liệu có thể làm?

  • Với Entity Framework, tiêu điểm được coi là một mô hình khái niệm. Đó là mô hình của các đối tượng trong ứng dụng của bạn, không phải là mô hình của cơ sở dữ liệu mà bạn sử dụng để duy trì dữ liệu ứng dụng của mình.

  • Mô hình khái niệm của bạn có thể phù hợp với lược đồ cơ sở dữ liệu của bạn hoặc nó có thể hoàn toàn khác.

  • Bạn có thể sử dụng Trình thiết kế trực quan để xác định mô hình khái niệm của mình, sau đó có thể tạo ra các lớp cuối cùng bạn sẽ sử dụng trong ứng dụng của mình.

  • Bạn chỉ có thể xác định các lớp của mình và sử dụng một tính năng của Entity Framework được gọi là Code First. Và sau đó Entity Framework sẽ hiểu được mô hình khái niệm.

Dù bằng cách nào, Entity Framework cũng tìm ra cách chuyển từ mô hình khái niệm sang cơ sở dữ liệu của bạn. Vì vậy, bạn có thể truy vấn các đối tượng mô hình khái niệm của mình và làm việc trực tiếp với chúng.

Đặc trưng

Sau đây là các tính năng cơ bản của Entity Framework. Danh sách này được tạo dựa trên các tính năng đáng chú ý nhất và cũng từ các câu hỏi thường gặp về Entity Framework.

  • Entity Framework là một công cụ của Microsoft.
  • Entity Framework đang được phát triển như một sản phẩm Mã nguồn mở.
  • Entity Framework không còn bị ràng buộc hoặc phụ thuộc vào chu kỳ phát hành .NET.
  • Hoạt động với bất kỳ cơ sở dữ liệu quan hệ nào với nhà cung cấp Khung thực thể hợp lệ.
  • Tạo lệnh SQL từ LINQ đến Thực thể.
  • Entity Framework sẽ tạo các truy vấn được tham số hóa.
  • Theo dõi các thay đổi đối với các đối tượng trong bộ nhớ.
  • Cho phép chèn, cập nhật và xóa tạo lệnh.
  • Làm việc với một mô hình trực quan hoặc với các lớp học của riêng bạn.
  • Entity Framework đã lưu trữ Hỗ trợ thủ tục.

Kiến trúc của Entity Framework, từ dưới lên, bao gồm những điều sau:

Nhà cung cấp dữ liệu

Đây là các nhà cung cấp nguồn cụ thể, nó trừu tượng hóa các giao diện ADO.NET để kết nối với cơ sở dữ liệu khi lập trình dựa trên lược đồ khái niệm.

Nó dịch các ngôn ngữ SQL phổ biến như LINQ thông qua cây lệnh sang biểu thức SQL gốc và thực thi nó trên hệ thống DBMS cụ thể.

Khách hàng thực thể

Lớp này hiển thị lớp thực thể lên lớp trên. Máy khách thực thể cung cấp khả năng cho các nhà phát triển làm việc với các thực thể ở dạng hàng và cột bằng cách sử dụng truy vấn SQL thực thể mà không cần tạo các lớp để biểu diễn lược đồ khái niệm. Máy khách thực thể hiển thị các lớp khung thực thể, là chức năng cốt lõi. Các lớp này được gọi là Mô hình Dữ liệu Thực thể.

  • Các Storage Layer chứa toàn bộ lược đồ cơ sở dữ liệu ở định dạng XML.

  • Các Entity Layer cũng là một tệp XML định nghĩa các thực thể và mối quan hệ.

  • Các Mapping layer là một tệp XML ánh xạ các thực thể và mối quan hệ được xác định ở lớp khái niệm với các mối quan hệ và bảng thực tế được xác định ở lớp logic.

  • Các Metadata services cũng được đại diện trong Entity Client cung cấp API tập trung để truy cập các lớp Thực thể, Ánh xạ và Lưu trữ siêu dữ liệu được lưu trữ.

Dịch vụ đối tượng

Lớp Dịch vụ Đối tượng là Ngữ cảnh Đối tượng, đại diện cho phiên tương tác giữa các ứng dụng và nguồn dữ liệu.

  • Công dụng chính của Object Context là thực hiện các thao tác khác nhau như thêm, xóa các phiên bản của thực thể và lưu trạng thái đã thay đổi trở lại cơ sở dữ liệu với sự trợ giúp của các truy vấn.

  • Đây là lớp ORM của Khung thực thể, biểu thị kết quả dữ liệu cho các thể hiện đối tượng của các thực thể.

  • Dịch vụ này cho phép nhà phát triển sử dụng một số tính năng ORM phong phú như ánh xạ khóa chính, theo dõi thay đổi, v.v. bằng cách viết các truy vấn bằng LINQ và Entity SQL.

Có gì mới trong Entity Framework 6?

Framework có một API phức tạp cho phép bạn kiểm soát chi tiết mọi thứ từ mô hình hóa đến hành vi thời gian chạy của nó. Một phần của Entity Framework 5 nằm bên trong .NET. Và một phần khác của nó nằm bên trong một lắp ráp bổ sung được phân phối bằng NuGet.

  • Chức năng cốt lõi của Entity Framework được tích hợp trong .NET Framework.

  • Hỗ trợ Code First, đó là thứ cho phép Entity Framework sử dụng các lớp thay cho mô hình trực quan và API cách nhẹ hơn để tương tác với EF có trong gói NuGet.

  • Cốt lõi là những gì cung cấp truy vấn, theo dõi thay đổi và tất cả các chuyển đổi từ truy vấn của bạn sang truy vấn SQL cũng như từ dữ liệu trả về thành các đối tượng.

  • Bạn có thể sử dụng gói EF 5 NuGet với cả .NET 4 và .NET 4.5.

  • Một điểm nhầm lẫn lớn - .NET 4.5 đã thêm hỗ trợ cho dữ liệu không gian và enums vào các API khung thực thể cốt lõi, có nghĩa là nếu bạn đang sử dụng EF 5 với .NET 4, bạn sẽ không nhận được các tính năng mới này. Bạn sẽ chỉ nhận được chúng khi kết hợp EF5 với .NET 4.5.

Bây giờ chúng ta hãy xem xét Entity Framework 6. Các API cốt lõi bên trong .NET trong Entity Framework 6 hiện là một phần của gói NuGet.

Nó có nghĩa là -

  • Tất cả các cuộc sống của Entity Framework đều chứa trong assembly này được phân phối bởi NuGet

  • Bạn sẽ không phụ thuộc vào .NET để cung cấp các tính năng cụ thể như hỗ trợ enum Entity Framework và hỗ trợ dữ liệu đặc biệt.

  • Bạn sẽ thấy rằng một trong những tính năng của EF6 là nó hỗ trợ dữ liệu không gian và môi trường cho .NET 4

Để bắt đầu làm việc trên Entity Framework, bạn cần cài đặt các công cụ phát triển sau:

  • Visual Studio 2013 trở lên
  • SQL Server 2012 trở lên
  • Cập nhật Khung thực thể từ Gói NuGet

Microsoft cung cấp phiên bản visual studio miễn phí cũng chứa SQL Server và có thể tải xuống từ www.visualstudio.com .

Cài đặt

Step 1- Sau khi tải xong, hãy chạy trình cài đặt. Hộp thoại sau sẽ được hiển thị.

Step 2 - Bấm vào nút Install và nó sẽ bắt đầu quá trình cài đặt.

Step 3- Khi quá trình cài đặt hoàn tất thành công, bạn sẽ thấy hộp thoại sau. Đóng hộp thoại này và khởi động lại máy tính của bạn nếu được yêu cầu.

Step 4- Mở Visual Studio từ Start Menu sẽ mở hộp thoại sau. Lần đầu tiên sẽ mất một khoảng thời gian để chuẩn bị.

Step 5 - Sau khi hoàn tất, bạn sẽ thấy cửa sổ chính của Visual studio.

Hãy tạo một dự án mới từ Tệp → Mới → Dự án

Step 1 - Chọn Ứng dụng Bảng điều khiển và nhấp vào nút OK.

Step 2 - Trong giải pháp Explorer, nhấp chuột phải vào dự án của bạn.

Step 3 - Chọn Manage NuGet Packages như trong hình trên, sẽ mở cửa sổ sau trong Visual Studio.

Step 4 - Tìm kiếm Entity Framework và cài đặt phiên bản mới nhất bằng cách nhấn nút cài đặt.

Step 5- Bấm Ok. Sau khi cài đặt xong, bạn sẽ thấy thông báo sau trong Cửa sổ đầu ra của mình.

Bây giờ bạn đã sẵn sàng để bắt đầu ứng dụng của mình.

Trong hướng dẫn này, chúng tôi sẽ sử dụng một cơ sở dữ liệu Đại học đơn giản. Cơ sở dữ liệu trường đại học có thể phức tạp hơn nhiều nhưng để phục vụ mục đích demo và học tập, chúng tôi đang sử dụng dạng đơn giản nhất của cơ sở dữ liệu này. Sơ đồ sau đây chứa ba bảng.

  • Student
  • Course
  • Enrollment

Bất cứ khi nào một cơ sở dữ liệu thuật ngữ được sử dụng, một điều sẽ xuất hiện trực tiếp trong tâm trí chúng ta và đó là các loại bảng khác nhau có mối quan hệ nào đó. Có ba loại mối quan hệ giữa các bảng và mối quan hệ giữa các bảng khác nhau phụ thuộc vào cách xác định các cột liên quan.

  • Mối quan hệ một-nhiều
  • Mối quan hệ nhiều-nhiều
  • Mối quan hệ một-một

Mối quan hệ một-nhiều

Mối quan hệ một - nhiều là kiểu quan hệ phổ biến nhất. Trong kiểu quan hệ này, một hàng trong bảng A có thể có nhiều hàng phù hợp trong bảng B, nhưng một hàng trong bảng B chỉ có thể có một hàng phù hợp trong bảng A. Ví dụ: trong sơ đồ trên, bảng Sinh viên và Ghi danh có một -tối quan hệ nhiều, mỗi sinh viên có thể có nhiều đăng ký, nhưng mỗi tuyển sinh chỉ thuộc về một sinh viên.

Mối quan hệ nhiều-nhiều

Trong mối quan hệ nhiều-nhiều, một hàng trong bảng A có thể có nhiều hàng phù hợp trong bảng B và ngược lại. Bạn tạo mối quan hệ như vậy bằng cách xác định bảng thứ ba, được gọi là bảng nối, có khóa chính bao gồm các khóa ngoại từ cả bảng A và bảng B. Ví dụ: bảng Sinh viên và khóa học có mối quan hệ nhiều-nhiều được xác định bởi mối quan hệ một-nhiều từ mỗi bảng này đến bảng Đăng ký.

Mối quan hệ một-một

Trong mối quan hệ một đối một, một hàng trong bảng A không thể có nhiều hơn một hàng phù hợp trong bảng B và ngược lại. Mối quan hệ một-một được tạo nếu cả hai cột liên quan đều là khóa chính hoặc có các ràng buộc duy nhất.

Loại mối quan hệ này không phổ biến vì hầu hết thông tin liên quan theo cách này sẽ là bảng tất cả trong một. Bạn có thể sử dụng mối quan hệ 1-1 để -

  • Chia một bảng có nhiều cột.
  • Cô lập một phần của bảng vì lý do bảo mật.
  • Lưu trữ dữ liệu tồn tại trong thời gian ngắn và có thể dễ dàng xóa bằng cách xóa bảng.
  • Lưu trữ thông tin chỉ áp dụng cho một tập hợp con của bảng chính.

Mô hình Dữ liệu Thực thể (EDM) là một phiên bản mở rộng của mô hình Mối quan hệ-Thực thể chỉ định mô hình khái niệm của dữ liệu bằng cách sử dụng các kỹ thuật mô hình hóa khác nhau. Nó cũng đề cập đến một tập hợp các khái niệm mô tả cấu trúc dữ liệu, bất kể hình thức được lưu trữ của nó là gì.

EDM hỗ trợ một tập hợp các kiểu dữ liệu nguyên thủy xác định các thuộc tính trong một mô hình khái niệm. Chúng ta cần xem xét 3 phần cốt lõi tạo nền tảng cho Khung thực thể và gọi chung là Mô hình Dữ liệu Thực thể. Sau đây là ba phần cốt lõi của EDM.

  • Mô hình giản đồ lưu trữ
  • Mô hình khái niệm
  • Mô hình ánh xạ

Mô hình giản đồ lưu trữ

Mô hình lưu trữ còn được gọi là Lớp định nghĩa lược đồ lưu trữ (SSDL) đại diện cho biểu đồ sơ đồ của kho dữ liệu phụ trợ.

Mô hình khái niệm

Mô hình khái niệm còn được gọi là Lớp định nghĩa lược đồ khái niệm (CSDL) là mô hình thực thể thực, dựa vào đó chúng ta viết các truy vấn của mình.

Mô hình ánh xạ

Mapping Layer chỉ là một ánh xạ giữa mô hình Khái niệm và mô hình Lưu trữ.

Lược đồ logic và ánh xạ của nó với lược đồ vật lý được biểu diễn dưới dạng EDM.

  • Visual Studio cũng cung cấp Trình thiết kế thực thể, để tạo hình ảnh của EDM và đặc tả ánh xạ.

  • Đầu ra của công cụ là tệp XML (* .edmx) chỉ định lược đồ và ánh xạ.

  • Tệp Edmx chứa các tạo tác siêu dữ liệu Entity Framework.

Ngôn ngữ định nghĩa lược đồ

Khung thực thể ADO.NET sử dụng Ngôn ngữ định nghĩa dữ liệu dựa trên XML được gọi là Ngôn ngữ định nghĩa lược đồ (SDL) để xác định lược đồ EDM.

  • SDL xác định các Kiểu đơn giản tương tự như các kiểu nguyên thủy khác, bao gồm Chuỗi, Int32, Đôi, Thập phân và DateTime, trong số những kiểu khác.

  • Enumeration, xác định một bản đồ các giá trị và tên nguyên thủy, cũng được coi là một kiểu đơn giản.

  • Các bảng kê chỉ được hỗ trợ từ phiên bản khung 5.0 trở đi.

  • Loại phức hợp được tạo ra từ tập hợp các loại khác. Tập hợp các thuộc tính của các kiểu này xác định một Kiểu Thực thể.

Mô hình dữ liệu chủ yếu có ba khái niệm chính để mô tả cấu trúc dữ liệu:

  • Loại thực thể
  • Loại liên kết
  • Property

Loại thực thể

Kiểu thực thể là khối xây dựng cơ bản để mô tả cấu trúc của dữ liệu trong EDM.

  • Trong mô hình khái niệm, các kiểu thực thể được xây dựng từ các thuộc tính và mô tả cấu trúc của các khái niệm cấp cao nhất, chẳng hạn như Sinh viên và Đăng ký trong một ứng dụng kinh doanh.

  • Một thực thể đại diện cho một đối tượng cụ thể chẳng hạn như Học sinh hoặc Ghi danh cụ thể.

  • Mỗi thực thể phải có một khóa thực thể duy nhất trong một tập thực thể. Tập thực thể là một tập hợp các thể hiện của một loại thực thể cụ thể. Các tập thực thể (và tập kết hợp) được nhóm một cách hợp lý trong một vùng chứa thực thể.

  • Kế thừa được hỗ trợ với các loại thực thể, nghĩa là, một loại thực thể có thể được bắt nguồn từ một loại thực thể khác.

Loại liên kết

Nó là một khối xây dựng cơ bản khác để mô tả các mối quan hệ trong EDM. Trong một mô hình khái niệm, một liên kết thể hiện mối quan hệ giữa hai loại thực thể như Sinh viên và Ghi danh.

  • Mọi liên kết đều có hai đầu kết hợp chỉ định các loại thực thể tham gia vào liên kết.

  • Mỗi phần cuối của liên kết cũng chỉ định đa dạng kết thúc liên kết cho biết số lượng thực thể có thể có ở phần cuối của liên kết đó.

  • Đa hiệu cuối liên kết có thể có giá trị là một (1), không hoặc một (0..1) hoặc nhiều (*).

  • Các thực thể ở một đầu của liên kết có thể được truy cập thông qua các thuộc tính điều hướng hoặc thông qua các khóa ngoại nếu chúng được hiển thị trên một loại thực thể.

Bất động sản

Các kiểu thực thể chứa các thuộc tính xác định cấu trúc và đặc điểm của chúng. Ví dụ: kiểu thực thể Sinh viên có thể có các thuộc tính như Mã sinh viên, Tên, v.v.

Thuộc tính có thể chứa dữ liệu nguyên thủy (chẳng hạn như chuỗi, số nguyên hoặc giá trị Boolean) hoặc dữ liệu có cấu trúc (chẳng hạn như kiểu phức hợp).

Khung thực thể cho phép bạn truy vấn, chèn, cập nhật và xóa dữ liệu, sử dụng các đối tượng Thời gian chạy ngôn ngữ chung (CLR) được gọi là thực thể. Khung thực thể ánh xạ các thực thể và mối quan hệ được xác định trong mô hình của bạn với cơ sở dữ liệu. Nó cũng cung cấp các tiện nghi để -

  • Vật chất hóa dữ liệu được trả về từ cơ sở dữ liệu dưới dạng các đối tượng thực thể
  • Theo dõi các thay đổi đã được thực hiện đối với các đối tượng
  • Xử lý đồng thời
  • Truyền các thay đổi đối tượng trở lại cơ sở dữ liệu
  • Ràng buộc các đối tượng với điều khiển

Lớp chính chịu trách nhiệm tương tác với dữ liệu dưới dạng các đối tượng là System.Data.Entity.DbContext. API DbContext không được phát hành như một phần của .NET Framework. Để linh hoạt và thường xuyên hơn với việc phát hành các tính năng mới cho Code First và API DbContext, nhóm Entity Framework phân phối EntityFramework.dll thông qua tính năng phân phối NuGet của Microsoft.

  • NuGet cho phép bạn thêm các tham chiếu đến các dự án .NET của mình bằng cách kéo trực tiếp các tệp DLL có liên quan vào dự án của bạn từ Web.

  • Tiện ích mở rộng Visual Studio được gọi là Trình quản lý gói thư viện cung cấp một cách dễ dàng để kéo tập hợp thích hợp từ Web vào các dự án của bạn.

  • API DbContext chủ yếu nhằm mục đích đơn giản hóa tương tác của bạn với Entity Framework.

  • Nó cũng làm giảm số lượng các phương thức và thuộc tính bạn cần để truy cập các tác vụ thường được sử dụng.

  • Trong các phiên bản trước của Entity Framework, các tác vụ này thường phức tạp để khám phá và viết mã.

  • Lớp ngữ cảnh quản lý các đối tượng thực thể trong thời gian chạy, bao gồm đưa các đối tượng vào dữ liệu từ cơ sở dữ liệu, theo dõi thay đổi và dữ liệu liên tục vào cơ sở dữ liệu.

Định nghĩa một lớp bắt nguồn từ DbContext

Cách được khuyến nghị để làm việc với ngữ cảnh là xác định một lớp dẫn xuất từ ​​DbContext và hiển thị các thuộc tính DbSet đại diện cho các tập hợp của các thực thể được chỉ định trong ngữ cảnh. Nếu bạn đang làm việc với EF Designer, ngữ cảnh sẽ được tạo cho bạn. Nếu bạn đang làm việc với Code First, bạn thường sẽ tự viết ngữ cảnh.

Đoạn mã sau là một ví dụ đơn giản cho thấy UniContext có nguồn gốc từ DbContext.

  • Bạn có thể sử dụng các thuộc tính tự động với DbSet như getter và setter.

  • Nó cũng làm cho mã sạch hơn nhiều, nhưng bạn không bắt buộc phải sử dụng nó cho mục đích tạo DbSet khi bạn không có logic nào khác để áp dụng.

public class UniContext : DbContext {
   public UniContext() : base("UniContext") { }
   public DbSet<Student> Students { get; set; }
   public DbSet<Enrollment> Enrollments { get; set; }
   public DbSet<Course> Courses { get; set; }
}
  • Trước đây, EDM được sử dụng để tạo các lớp ngữ cảnh có nguồn gốc từ lớp ObjectContext.

  • Làm việc với ObjectContext hơi phức tạp.

  • DbContext là một trình bao bọc xung quanh ObjectContext thực sự tương tự như ObjectContext và rất hữu ích và dễ dàng trong tất cả các mô hình phát triển như Code First, Model First và Database First.

Truy vấn

Có ba loại truy vấn bạn có thể sử dụng, chẳng hạn như -

  • Thêm một thực thể mới.
  • Thay đổi hoặc cập nhật các giá trị thuộc tính của một thực thể hiện có.
  • Xóa một thực thể hiện có.

Thêm thực thể mới

Thêm một đối tượng mới với Entity Framework đơn giản như việc xây dựng một phiên bản mới của đối tượng của bạn và đăng ký nó bằng phương pháp Add trên DbSet. Đoạn mã sau dành cho khi bạn muốn thêm một học sinh mới vào cơ sở dữ liệu.

private static void AddStudent() {

   using (var context = new UniContext()) {

      var student = new Student {
         LastName = "Khan", 
         FirstMidName = "Ali", 
         EnrollmentDate = DateTime.Parse("2005-09-01") 
      };

      context.Students.Add(student); 
      context.SaveChanges();

   }
}

Thay đổi các thực thể hiện có

Thay đổi các đối tượng hiện có đơn giản như cập nhật giá trị được gán cho (các) thuộc tính bạn muốn thay đổi và gọi SaveChanges. Trong đoạn mã sau, họ của Ali đã được đổi từ Khan thành Aslam.

private static void AddStudent() {

   private static void ChangeStudent() {

      using (var context = new UniContext()) {

         var student = (from d in context.Students
            where d.FirstMidName == "Ali" select d).Single();
         student.LastName = "Aslam";
         context.SaveChanges();

      }
   }
}

Xóa các đối tượng hiện có

Để xóa một thực thể bằng Entity Framework, bạn sử dụng phương pháp Remove trên DbSet. Xóa tác phẩm cho cả thực thể hiện có và thực thể mới được thêm vào. Gọi Xóa trên một thực thể đã được thêm nhưng chưa được lưu vào cơ sở dữ liệu sẽ hủy bỏ việc thêm đối tượng. Thực thể bị xóa khỏi trình theo dõi thay đổi và không còn được theo dõi bởi DbContext. Việc gọi Xóa trên một thực thể hiện có đang được theo dõi thay đổi sẽ đăng ký thực thể để xóa vào lần gọi SaveChanges tiếp theo. Ví dụ sau đây cho thấy một trường hợp mà học sinh bị xóa khỏi cơ sở dữ liệu có tên đầu tiên là Ali.

private static void DeleteStudent() {

   using (var context = new UniContext()) {
      var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
      context.Students.Remove(bay);
      context.SaveChanges();
   }
}

Trong Entity Framework, có hai loại thực thể cho phép các nhà phát triển sử dụng các lớp dữ liệu tùy chỉnh của riêng họ cùng với mô hình dữ liệu mà không cần thực hiện bất kỳ sửa đổi nào đối với chính các lớp dữ liệu.

  • Thực thể POCO
  • Proxy động

Thực thể POCO

  • POCO là viết tắt của các đối tượng CLR "cũ kỹ" có thể được sử dụng như các đối tượng miền hiện có với mô hình dữ liệu của bạn.

  • Các lớp dữ liệu POCO được ánh xạ tới các thực thể được định nghĩa trong một mô hình dữ liệu.

  • Nó cũng hỗ trợ hầu hết các hành vi truy vấn, chèn, cập nhật và xóa giống như các loại thực thể được tạo bởi các công cụ Mô hình Dữ liệu Thực thể.

  • Bạn có thể sử dụng mẫu POCO để tạo các loại thực thể không biết tồn tại từ một mô hình khái niệm.

Chúng ta hãy xem ví dụ sau về Mô hình Dữ liệu Thực thể Khái niệm.

Để tạo các thực thể POCO cho mô hình Thực thể ở trên -

Step 1- Nhấp chuột phải vào cửa sổ trình thiết kế. Nó sẽ hiển thị hộp thoại sau.

Step 2 - Chọn mục Thêm mã tạo mã ...

Step 3 - Chọn EF 6.x DbContext Generator, viết tên và sau đó nhấp vào nút Thêm.

Bạn sẽ thấy trong trình khám phá giải pháp của mình rằng các mẫu POCODemo.Context.tt và POCODemo.tt được tạo.

POCODemo.Context tạo DbContext và các tập đối tượng mà bạn có thể trả về và sử dụng để truy vấn, chẳng hạn như cho ngữ cảnh, Sinh viên và Khóa học, v.v.

Mẫu khác đề cập đến tất cả các loại Sinh viên, Khóa học, v.v. Sau đây là mã cho lớp Sinh viên được tạo tự động từ Mô hình thực thể.

namespace ConsoleApplication1 {

   using System;
   using System.Collections.Generic;

   public partial class Student {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Student() {
         this.Enrollments = new HashSet<Enrollment>();
      }

      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public System.DateTime EnrollmentDate { get; set; }

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         CA2227:CollectionPropertiesShouldBeReadOnly")]

      public virtual ICollection<Enrollment> Enrollments { get; set; }

   }
}

Các lớp tương tự được tạo cho các bảng Khóa học và Ghi danh từ Mô hình Thực thể.

Proxy động

Khi tạo các phiên bản của loại thực thể POCO, Khung thực thể thường tạo các phiên bản của loại dẫn xuất được tạo động hoạt động như một proxy cho thực thể. CNTT cũng có thể nói rằng nó là một lớp proxy thời gian chạy giống như một lớp trình bao bọc của thực thể POCO.

  • Bạn có thể ghi đè một số thuộc tính của thực thể để thực hiện các hành động tự động khi thuộc tính được truy cập.

  • Cơ chế này được sử dụng để hỗ trợ tải chậm các mối quan hệ và theo dõi thay đổi tự động.

  • Kỹ thuật này cũng áp dụng cho những mô hình được tạo bằng Code First và EF Designer.

Nếu bạn muốn Khung thực thể hỗ trợ tải chậm các đối tượng liên quan và theo dõi các thay đổi trong các lớp POCO, thì các lớp POCO phải đáp ứng các yêu cầu sau:

  • Lớp dữ liệu tùy chỉnh phải được khai báo với quyền truy cập công khai.

  • Lớp dữ liệu tùy chỉnh không được niêm phong.

  • Lớp dữ liệu tùy chỉnh không được trừu tượng.

  • Lớp dữ liệu tùy chỉnh phải có một phương thức khởi tạo công khai hoặc được bảo vệ không có tham số.

  • Sử dụng một hàm tạo được bảo vệ không có tham số nếu bạn muốn phương thức CreateObject được sử dụng để tạo proxy cho thực thể POCO.

  • Việc gọi phương thức CreateObject không đảm bảo việc tạo proxy: lớp POCO phải tuân theo các yêu cầu khác được mô tả trong chủ đề này.

  • Lớp không thể triển khai các giao diện IEntityWithChangeTracker hoặc IEntityWithRelationships vì các lớp proxy thực thi các giao diện này.

  • Tùy chọn ProxyCreationEnabled phải được đặt thành true.

Ví dụ sau đây là về lớp thực thể proxy động.

public partial class Course {

   public Course() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Để tắt tạo đối tượng proxy, hãy đặt giá trị của thuộc tính ProxyCreationEnabled thành false.

Trong cơ sở dữ liệu quan hệ, mối quan hệ là một tình huống tồn tại giữa các bảng cơ sở dữ liệu quan hệ thông qua các khóa ngoại. Khóa ngoại (FK) là một cột hoặc tổ hợp các cột được sử dụng để thiết lập và thực thi liên kết giữa dữ liệu trong hai bảng. Sơ đồ sau đây chứa ba bảng.

  • Student
  • Course
  • Enrollment

Trong sơ đồ trên, bạn có thể thấy một số loại liên kết / quan hệ giữa các bảng. Có ba loại mối quan hệ giữa các bảng và mối quan hệ giữa các bảng khác nhau phụ thuộc vào cách xác định các cột liên quan.

  • Mối quan hệ một-nhiều
  • Mối quan hệ nhiều-nhiều
  • Mối quan hệ một-một

Mối quan hệ một-nhiều

  • Mối quan hệ một - nhiều là loại mối quan hệ phổ biến nhất.

  • Trong kiểu quan hệ này, một hàng trong bảng A có thể có nhiều hàng phù hợp trong bảng B, nhưng một hàng trong bảng B chỉ có thể có một hàng phù hợp trong bảng A.

  • Khóa ngoại được định nghĩa trong bảng đại diện cho nhiều phần cuối của mối quan hệ.

  • Ví dụ, trong sơ đồ trên, bảng Student và Enrollment có mối quan hệ một bên, mỗi học sinh có thể có nhiều đăng ký, nhưng mỗi ghi danh chỉ thuộc về một học sinh.

Trong khuôn khổ thực thể, mối quan hệ này cũng có thể được tạo bằng mã. Sau đây là một ví dụ về các lớp Sinh viên và Ghi danh được liên kết với mối quan hệ từ một đến nhiều.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Enrollment {

   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
	
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

Trong đoạn mã trên, bạn có thể thấy rằng lớp Sinh viên chứa tập hợp các Đăng ký, nhưng lớp Đăng ký có một Đối tượng Sinh viên duy nhất.

Mối quan hệ nhiều-nhiều

Trong mối quan hệ nhiều-nhiều, một hàng trong bảng A có thể có nhiều hàng phù hợp trong bảng B và ngược lại.

  • Bạn có thể tạo một mối quan hệ như vậy bằng cách xác định bảng thứ ba, được gọi là bảng nối, có khóa chính bao gồm các khóa ngoại từ cả bảng A và bảng B.

  • Ví dụ: các bảng Sinh viên và Khóa học có mối quan hệ nhiều-nhiều được xác định bởi mối quan hệ một-nhiều từ mỗi bảng này đến bảng Ghi danh.

Đoạn mã sau chứa lớp Khóa học và hai lớp trên, tức là StudentEnrollment.

public class Course {
   [DatabaseGenerated(DatabaseGeneratedOption.None)]
	
   public int CourseID { get; set; }
   public string Title { get; set; }
	
   public int Credits { get; set; } 
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bạn có thể thấy rằng cả lớp Khóa học và lớp Sinh viên đều có các tập hợp các đối tượng Đăng ký tạo mối quan hệ nhiều-nhiều thông qua Đăng ký lớp nối.

Mối quan hệ một-một

  • Trong mối quan hệ 1-1, một hàng trong bảng A không thể có nhiều hơn một hàng phù hợp trong bảng B và ngược lại.

  • Mối quan hệ một-một được tạo nếu cả hai cột liên quan đều là khóa chính hoặc có các ràng buộc duy nhất.

  • Trong mối quan hệ một-một, khóa chính hoạt động bổ sung như một khóa ngoại và không có cột khóa ngoại riêng biệt cho cả hai bảng.

Kiểu quan hệ này không phổ biến vì hầu hết thông tin liên quan theo cách này sẽ nằm trong một bảng. Bạn có thể sử dụng mối quan hệ 1-1 để -

  • Chia một bảng có nhiều cột.
  • Cô lập một phần của bảng vì lý do bảo mật.
  • Lưu trữ dữ liệu tồn tại trong thời gian ngắn và có thể dễ dàng xóa bằng cách xóa bảng.
  • Lưu trữ thông tin chỉ áp dụng cho một tập hợp con của bảng chính.

Đoạn mã sau đây là để thêm một tên lớp khác StudentProfile có chứa id email và mật khẩu của sinh viên.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
   public virtual StudentProfile StudentProfile { get; set; }
}

public class StudentProfile {

   public StudentProfile() {}
   public int ID { get; set; }
   public string Email { get; set; }
   public string Password { get; set; }
	
   public virtual Student Student { get; set; }
}

Bạn có thể thấy rằng lớp thực thể Student chứa thuộc tính điều hướng StudentProfile và StudentProfile chứa thuộc tính điều hướng Student.

Mỗi sinh viên chỉ có một Email và mật khẩu để đăng nhập vào miền đại học. Những thông tin này có thể được thêm vào bảng Sinh viên nhưng vì lý do bảo mật, nó được tách ra một bảng khác.

Cả đời

Thời gian tồn tại của ngữ cảnh bắt đầu khi cá thể được tạo và kết thúc khi cá thể đó được xử lý hoặc thu gom rác.

  • Thời gian tồn tại theo ngữ cảnh là một quyết định rất quan trọng khi chúng ta sử dụng ORM.

  • Bối cảnh đang hoạt động giống như một bộ đệm ẩn thực thể, vì vậy nó có nghĩa là nó chứa các tham chiếu đến tất cả các thực thể được tải có thể phát triển rất nhanh trong việc tiêu thụ bộ nhớ và nó cũng có thể gây rò rỉ bộ nhớ.

  • Trong sơ đồ dưới đây, bạn có thể thấy quy trình làm việc dữ liệu cấp trên từ ứng dụng đến cơ sở dữ liệu thông qua Ngữ cảnh và ngược lại.

Vòng đời thực thể

Vòng đời Thực thể mô tả quá trình trong đó Thực thể được tạo, thêm, sửa đổi, xóa, v.v. Thực thể có nhiều trạng thái trong suốt thời gian tồn tại của nó. Trước khi xem cách truy xuất trạng thái thực thể, chúng ta hãy xem trạng thái thực thể là gì. Trạng thái là một kiểu enumSystem.Data.EntityState khai báo các giá trị sau:

  • Added: Thực thể được đánh dấu là đã thêm.

  • Deleted: Thực thể được đánh dấu là đã xóa.

  • Modified: Thực thể đã được sửa đổi.

  • Unchanged: Thực thể chưa được sửa đổi.

  • Detached: Thực thể không được theo dõi.

Thay đổi trạng thái trong vòng đời thực thể

Đôi khi trạng thái của các thực thể được đặt tự động bởi ngữ cảnh, nhưng nó cũng có thể được nhà phát triển sửa đổi theo cách thủ công. Mặc dù tất cả các tổ hợp chuyển đổi từ trạng thái này sang trạng thái khác đều có thể thực hiện được, nhưng một số trong số chúng là vô nghĩa. Ví dụ,Added thực thể cho Deleted trạng thái, hoặc ngược lại.

Hãy thảo luận về các trạng thái khác nhau.

Trạng thái không thay đổi

  • Khi một thực thể là Không thay đổi, nó bị ràng buộc với ngữ cảnh nhưng nó chưa được sửa đổi.

  • Theo mặc định, một thực thể được truy xuất từ ​​cơ sở dữ liệu ở trạng thái này.

  • Khi một thực thể được gắn vào ngữ cảnh (với phương thức Đính kèm), nó tương tự ở trạng thái Không thay đổi.

  • Ngữ cảnh không thể theo dõi các thay đổi đối với các đối tượng mà nó không tham chiếu, vì vậy khi chúng được đính kèm, nó sẽ giả định rằng chúng Không thay đổi.

Trạng thái tách biệt

  • Tách biệt là trạng thái mặc định của một thực thể mới được tạo vì ngữ cảnh không thể theo dõi việc tạo bất kỳ đối tượng nào trong mã của bạn.

  • Điều này đúng ngay cả khi bạn khởi tạo thực thể bên trong một khối ngữ cảnh đang sử dụng.

  • Tách rời thậm chí là trạng thái của các thực thể được truy xuất từ ​​cơ sở dữ liệu khi tính năng theo dõi bị tắt.

  • Khi một thực thể được tách ra, nó không bị ràng buộc với ngữ cảnh, vì vậy trạng thái của nó không được theo dõi.

  • Nó có thể được loại bỏ, sửa đổi, sử dụng kết hợp với các lớp khác hoặc sử dụng theo bất kỳ cách nào khác mà bạn có thể cần.

  • Bởi vì không có ngữ cảnh theo dõi nó, nó không có ý nghĩa gì đối với Entity Framework.

Đã thêm trạng thái

  • Khi một thực thể ở trạng thái Đã thêm, bạn có một số tùy chọn. Trên thực tế, bạn chỉ có thể tách nó ra khỏi ngữ cảnh.

  • Đương nhiên, ngay cả khi bạn sửa đổi một số thuộc tính, trạng thái vẫn được Thêm vào, bởi vì việc chuyển nó sang Đã sửa đổi, Không thay đổi hoặc Đã xóa không có ý nghĩa gì.

  • Đó là một thực thể mới và không có sự tương ứng với một hàng trong cơ sở dữ liệu.

  • Đây là điều kiện tiên quyết cơ bản để ở một trong những trạng thái đó (nhưng quy tắc này không được thực thi theo ngữ cảnh).

Trạng thái sửa đổi

  • Khi một thực thể được sửa đổi, điều đó có nghĩa là nó ở trạng thái Không thay đổi và sau đó một số thuộc tính đã được thay đổi.

  • Sau khi một thực thể chuyển sang trạng thái Đã sửa đổi, nó có thể chuyển sang trạng thái Đã tách hoặc Đã xóa, nhưng không thể quay trở lại trạng thái Không thay đổi ngay cả khi bạn khôi phục các giá trị ban đầu theo cách thủ công.

  • Nó thậm chí không thể được thay đổi thành Đã thêm, trừ khi bạn tách và thêm thực thể vào ngữ cảnh, vì một hàng có ID này đã tồn tại trong cơ sở dữ liệu và bạn sẽ nhận được ngoại lệ thời gian chạy khi duy trì nó.

Trạng thái đã xóa

  • Một thực thể đi vào trạng thái Đã xóa vì nó không được thay đổi hoặc sửa đổi và sau đó phương pháp DeleteObject được sử dụng.

  • Đây là trạng thái hạn chế nhất, bởi vì nó vô nghĩa thay đổi từ trạng thái này sang bất kỳ giá trị nào khác nhưng tách rời.

Các usingnếu bạn muốn tất cả các tài nguyên mà ngữ cảnh kiểm soát được xử lý ở cuối khối. Khi bạn sử dụngusing câu lệnh, sau đó trình biên dịch tự động tạo một khối thử / cuối cùng và các lệnh gọi vứt bỏ trong khối cuối cùng.

using (var context = new UniContext()) {

   var student = new Student {
      LastName = "Khan", 
      FirstMidName = "Ali", 
      EnrollmentDate = DateTime.Parse("2005-09-01")
   };

   context.Students.Add(student);
   context.SaveChanges();
}

Khi làm việc với ngữ cảnh dài hạn, hãy xem xét những điều sau:

  • Khi bạn tải nhiều đối tượng hơn và các tham chiếu của chúng vào bộ nhớ, mức tiêu thụ bộ nhớ của ngữ cảnh có thể tăng lên nhanh chóng. Điều này có thể gây ra các vấn đề về hiệu suất.

  • Hãy nhớ loại bỏ ngữ cảnh khi nó không còn được yêu cầu.

  • Nếu một ngoại lệ khiến ngữ cảnh ở trạng thái không thể khôi phục, toàn bộ ứng dụng có thể chấm dứt.

  • Cơ hội gặp phải các vấn đề liên quan đến đồng thời tăng lên khi khoảng cách giữa thời gian dữ liệu được truy vấn và cập nhật tăng lên.

  • Khi làm việc với các ứng dụng Web, hãy sử dụng một phiên bản ngữ cảnh cho mỗi yêu cầu.

  • Khi làm việc với Windows Presentation Foundation (WPF) hoặc Windows Forms, hãy sử dụng một phiên bản ngữ cảnh cho mỗi biểu mẫu. Điều này cho phép bạn sử dụng chức năng theo dõi thay đổi mà ngữ cảnh cung cấp.

Quy tắc của ngón tay cái

Web Applications

  • Hiện nay, một phương pháp phổ biến và tốt nhất là đối với các ứng dụng web, ngữ cảnh được sử dụng theo yêu cầu.

  • Trong các ứng dụng web, chúng tôi xử lý các yêu cầu rất ngắn nhưng giữ tất cả giao dịch máy chủ, do đó chúng là khoảng thời gian thích hợp cho bối cảnh tồn tại.

Desktop Applications

  • Đối với ứng dụng dành cho máy tính để bàn, như Win Forms / WPF, v.v., ngữ cảnh được sử dụng cho mỗi biểu mẫu / hộp thoại / trang.

  • Vì chúng tôi không muốn có ngữ cảnh như một singleton cho ứng dụng của mình, chúng tôi sẽ loại bỏ nó khi chúng tôi chuyển từ dạng này sang dạng khác.

  • Bằng cách này, chúng ta sẽ có được rất nhiều khả năng của bối cảnh và không bị ảnh hưởng bởi các bối cảnh dài hạn.

Khung thực thể cung cấp ba cách tiếp cận để tạo một mô hình thực thể và mỗi cách tiếp cận đều có ưu và nhược điểm riêng.

  • Mã đầu tiên
  • Cơ sở dữ liệu đầu tiên
  • Mô hình đầu tiên

Trong chương này, chúng tôi sẽ mô tả ngắn gọn cách tiếp cận mã đầu tiên. Một số nhà phát triển thích làm việc với Designer trong Code trong khi những người khác chỉ thích làm việc với mã của họ. Đối với những nhà phát triển đó, Entity Framework có quy trình làm việc mô hình hóa được gọi là Code First.

  • Dòng công việc lập mô hình Code First nhắm mục tiêu đến một cơ sở dữ liệu không tồn tại và Code First sẽ tạo nó.

  • Nó cũng có thể được sử dụng nếu bạn có một cơ sở dữ liệu trống và sau đó Code First cũng sẽ thêm các bảng mới.

  • Code First cho phép bạn xác định mô hình của mình bằng cách sử dụng các lớp C # hoặc VB.Net.

  • Cấu hình bổ sung có thể được thực hiện tùy chọn bằng cách sử dụng các thuộc tính trên các lớp và thuộc tính của bạn hoặc bằng cách sử dụng một API thông thạo.

Tại sao phải viết mã đầu tiên?

  • Code First thực sự được tạo thành từ một tập hợp các mảnh ghép. Đầu tiên là các lớp miền của bạn.

  • Các lớp miền không liên quan gì đến Entity Framework. Chúng chỉ là các mặt hàng thuộc miền doanh nghiệp của bạn.

  • Sau đó, Entity Framework có bối cảnh quản lý sự tương tác giữa các lớp đó và cơ sở dữ liệu của bạn.

  • Bối cảnh không dành riêng cho Code First. Đó là một tính năng Khung thực thể.

  • Code First thêm một trình tạo mô hình kiểm tra các lớp của bạn mà ngữ cảnh đang quản lý, sau đó sử dụng một tập hợp các quy tắc hoặc quy ước để xác định cách các lớp đó và các mối quan hệ mô tả một mô hình và cách mô hình đó sẽ ánh xạ đến cơ sở dữ liệu của bạn.

  • Tất cả điều này xảy ra trong thời gian chạy. Bạn sẽ không bao giờ nhìn thấy mô hình này, nó chỉ là trong bộ nhớ.

  • Code First có khả năng sử dụng mô hình đó để tạo cơ sở dữ liệu nếu được yêu cầu.

  • Nó cũng có thể cập nhật cơ sở dữ liệu nếu mô hình thay đổi, sử dụng một tính năng được gọi là Code First Migrations.

Trong chương này, chúng ta hãy tìm hiểu cách tạo một mô hình dữ liệu thực thể trong trình thiết kế bằng cách sử dụng quy trình làm việc được gọi là Model First.

  • Model First rất phù hợp khi bạn bắt đầu một dự án mới mà cơ sở dữ liệu chưa tồn tại.

  • Mô hình được lưu trữ trong tệp EDMX và có thể được xem và chỉnh sửa trong Trình thiết kế khung thực thể.

  • Trong Model First, bạn xác định mô hình của mình trong trình thiết kế Entity Framework, sau đó tạo SQL, thao tác này sẽ tạo lược đồ cơ sở dữ liệu để khớp với mô hình của bạn và sau đó bạn thực thi SQL để tạo lược đồ trong cơ sở dữ liệu của mình.

  • Các lớp mà bạn tương tác trong ứng dụng của mình được tạo tự động từ tệp EDMX.

Sau đây là một ví dụ đơn giản về việc tạo một dự án bảng điều khiển mới bằng cách sử dụng cách tiếp cận Model First.

Step 1 - Mở Visual Studio và chọn Tệp → Mới → Dự án

Step 2 - Chọn Đã cài đặt → Mẫu → Visual C # → Windows từ ngăn bên trái, sau đó ở ngăn giữa, chọn Ứng dụng bảng điều khiển.

Step 3 - Nhập EFModelFirstDemo vào trường Tên.

Step 4 - Để tạo mô hình, đầu tiên nhấp chuột phải vào dự án bảng điều khiển của bạn trong trình khám phá giải pháp và chọn Thêm → Mục mới…

Hộp thoại sau sẽ mở ra.

Step 5 - Chọn ADO.NET Entity Data Model từ khung giữa và nhập tên ModelFirstDemoDB vào trường Name.

Step 6 - Nhấp vào nút Thêm sẽ khởi chạy hộp thoại Trình hướng dẫn mô hình dữ liệu thực thể.

Step 7- Chọn mô hình Empty EF Designer và nhấp vào nút Next. Trình thiết kế khung thực thể mở ra với một mô hình trống. Bây giờ chúng ta có thể bắt đầu thêm các thực thể, thuộc tính và liên kết vào mô hình.

Step 8- Nhấp chuột phải vào bề mặt thiết kế và chọn Properties. Trong cửa sổ Thuộc tính, thay đổi Tên vùng chứa thực thể thành ModelFirstDemoDBContext.

Step 9 - Nhấp chuột phải vào bề mặt thiết kế và chọn Thêm mới → Thực thể…

Hộp thoại Thêm Thực thể sẽ mở ra như trong hình sau.

Step 10 - Nhập Sinh viên làm tên thực thể và Id Sinh viên làm tên thuộc tính và nhấp Ok.

Step 11 - Nhấp chuột phải vào thực thể mới trên bề mặt thiết kế và chọn Add New → Scalar Property, nhập Name là tên của thuộc tính.

Step 12 - Nhập FirstName và sau đó thêm hai thuộc tính vô hướng khác như LastName và EnrollmentDate.

Step 13 - Thêm hai Entities Course và Enrollment nữa bằng cách làm theo tất cả các bước nêu trên và cũng thêm một số thuộc tính Scalar như trong các bước sau.

Step 14 - Chúng tôi có ba thực thể trong Visual Designer, hãy thêm một số liên kết hoặc mối quan hệ giữa chúng.

Step 15 - Nhấp chuột phải vào bề mặt thiết kế và chọn Thêm mới → Liên kết…

Step 16 - Tạo một điểm cuối của mối quan hệ tới Sinh viên với bội số của một và điểm cuối còn lại là Điểm đăng ký với bội số.

Step 17 - Điều này có nghĩa là một Học sinh có nhiều Ghi danh và Ghi danh thuộc về một Học sinh.

Step 18 - Đảm bảo hộp Thêm thuộc tính khóa ngoại vào Thực thể 'Đăng' được chọn và nhấp vào OK.

Step 19 - Tương tự, thêm một liên kết nữa giữa Khóa học và Ghi danh.

Step 20 - Mô hình dữ liệu của bạn sẽ giống như màn hình sau đây sau khi thêm các liên kết giữa các thực thể.

Bây giờ chúng ta có một mô hình đơn giản mà chúng ta có thể tạo cơ sở dữ liệu từ đó và sử dụng để đọc và ghi dữ liệu. Hãy tiếp tục và tạo cơ sở dữ liệu.

Step 1 - Nhấp chuột phải vào bề mặt thiết kế và chọn Tạo cơ sở dữ liệu từ Mô hình…

Step 2 - Bạn có thể chọn cơ sở dữ liệu hiện có hoặc tạo kết nối mới bằng cách nhấp vào Kết nối mới…

Step 3 - Để tạo Cơ sở dữ liệu mới, nhấp vào Kết nối Mới…

Step 4 - Nhập Tên máy chủ và tên cơ sở dữ liệu.

Step 5 - Nhấp vào Tiếp theo.

Step 6- Nhấp vào Kết thúc. Điều này sẽ thêm tệp * .edmx.sql trong dự án. Bạn có thể thực thi các tập lệnh DDL trong Visual Studio bằng cách mở tệp .sql, sau đó nhấp chuột phải và chọn Thực thi.

Step 7 - Hộp thoại sau sẽ được hiển thị để kết nối với cơ sở dữ liệu.

Step 8 - Khi thực hiện thành công, bạn sẽ thấy thông báo sau.

Step 9 - Vào server explorer, bạn sẽ thấy cơ sở dữ liệu được tạo với ba bảng được chỉ định.

Tiếp theo, chúng ta cần hoán đổi mô hình của mình để tạo mã sử dụng API DbContext.

Step 1 - Nhấp chuột phải vào vị trí trống của mô hình của bạn trong EF Designer và chọn Thêm mục tạo mã…

Bạn sẽ thấy rằng hộp thoại Thêm mục mới sau đây sẽ mở ra.

Step 2 - Chọn EF 6.x DbContext Generator ở khung giữa và nhập ModelFirstDemoModel vào trường Tên.

Step 3 - Bạn sẽ thấy trong trình khám phá giải pháp của mình có các mẫu ModelFirstDemoModel.Context.tt và ModelFirstDemoModel.tt được tạo.

ModelFirstDemoModel.Context tạo DbCcontext và các tập đối tượng mà bạn có thể trả về và sử dụng để truy vấn, chẳng hạn như cho ngữ cảnh, Sinh viên và Khóa học, v.v.

Mẫu khác đề cập đến tất cả các loại Sinh viên, Khóa học, v.v. Sau đây là lớp Sinh viên, được tạo tự động từ Mô hình thực thể.

Sau đây là mã C # trong đó một số dữ liệu được nhập và truy xuất từ ​​cơ sở dữ liệu.

using System;
using System.Linq;

namespace EFModelFirstDemo {

   class Program {

      static void Main(string[] args) {

         using (var db = new ModelFirstDemoDBContext()) {

            // Create and save a new Student

            Console.Write("Enter a name for a new Student: ");
            var firstName = Console.ReadLine();

            var student = new Student {
               StudentID = 1,
               FirstName = firstName
            };
				
            db.Students.Add(student);
            db.SaveChanges();
				
            var query = from b in db.Students
               orderby b.FirstName select b;

            Console.WriteLine("All student in the database:");

            foreach (var item in query) {
               Console.WriteLine(item.FirstName);
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
         }
      }
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

Enter a name for a new Student:
Ali Khan
All student in the database:
Ali Khan
Press any key to exit...

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong chương này, chúng ta hãy tìm hiểu về cách tạo mô hình dữ liệu thực thể với cách tiếp cận Cơ sở dữ liệu đầu tiên.

  • Phương pháp tiếp cận cơ sở dữ liệu đầu tiên cung cấp một giải pháp thay thế cho các phương pháp tiếp cận Mã đầu tiên và Mô hình đầu tiên cho Mô hình dữ liệu thực thể. Nó tạo ra các mã mô hình (lớp, thuộc tính, DbContext, v.v.) từ cơ sở dữ liệu trong dự án và các lớp đó trở thành liên kết giữa cơ sở dữ liệu và bộ điều khiển.

  • Phương pháp Tiếp cận Cơ sở dữ liệu Đầu tiên tạo ra khung thực thể từ một cơ sở dữ liệu hiện có. Chúng tôi sử dụng tất cả các chức năng khác, chẳng hạn như đồng bộ hóa mô hình / cơ sở dữ liệu và tạo mã, giống như cách chúng tôi đã sử dụng chúng trong cách tiếp cận Mô hình Đầu tiên.

Hãy lấy một ví dụ đơn giản. Chúng ta đã có một cơ sở dữ liệu gồm 3 bảng như trong hình sau.

Step 1 - Hãy tạo một dự án console mới với tên DatabaseFirstDemo.

Step 2 - Để tạo mô hình, trước tiên hãy nhấp chuột phải vào dự án bảng điều khiển của bạn trong trình khám phá giải pháp và chọn Thêm → Mục mới…

Step 3 - Chọn ADO.NET Entity Data Model từ khung giữa và nhập tên DatabaseFirstModel vào trường Name.

Step 4 - Nhấp vào nút Thêm sẽ khởi chạy hộp thoại Trình hướng dẫn mô hình dữ liệu thực thể.

Step 5 - Chọn EF Designer từ cơ sở dữ liệu và nhấp vào nút Tiếp theo.

Step 6 - Chọn cơ sở dữ liệu hiện có và nhấp vào Tiếp theo.

Step 7 - Chọn Entity Framework 6.x và nhấp vào Tiếp theo.

Step 8 - Chọn tất cả các bảng Dạng xem và thủ tục đã lưu trữ mà bạn muốn đưa vào và nhấp vào Kết thúc.

Bạn sẽ thấy rằng mô hình Thực thể và các lớp POCO được tạo từ cơ sở dữ liệu.

Bây giờ chúng ta hãy truy xuất tất cả các sinh viên từ cơ sở dữ liệu bằng cách viết đoạn mã sau vào tệp program.cs.

using System;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         using (var db = new UniContextEntities()) {

            var query = from b in db.Students
               orderby b.FirstMidName select b;

            Console.WriteLine("All All student in the database:");

            foreach (var item in query) {
               Console.WriteLine(item.FirstMidName +" "+ item.LastName);
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
         }
      }
   }
}

Khi chương trình trên được thực thi, bạn sẽ nhận được kết quả sau:

All student in the database:
Ali Khan
Arturo   finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...

Khi chương trình trên được thực hiện, bạn sẽ thấy tất cả tên của học sinh đã được nhập trước đó trong cơ sở dữ liệu.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong chương này, chúng ta hãy tập trung vào việc xây dựng các mô hình với Trình thiết kế hoặc Cơ sở dữ liệu Đầu tiên hoặc chỉ sử dụng Mã Đầu tiên. Sau đây là một số nguyên tắc sẽ giúp bạn quyết định chọn quy trình làm việc mô hình hóa nào.

  • Chúng tôi đã thấy các ví dụ về lập mô hình Mã đầu tiên, mô hình hóa Cơ sở dữ liệu đầu tiên và quy trình làm việc mô hình hóa Mô hình đầu tiên.

  • Luồng công việc Cơ sở dữ liệu đầu tiên và Mô hình đầu tiên sử dụng Trình thiết kế nhưng một luồng bắt đầu với cơ sở dữ liệu để tạo mô hình và luồng kia bắt đầu từ mô hình để tạo cơ sở dữ liệu.

  • Đối với những nhà phát triển không muốn sử dụng Visual Designer cộng với tạo mã, Entity Framework có một quy trình làm việc hoàn toàn khác được gọi là Code First.

  • Quy trình làm việc điển hình cho Code First rất phù hợp cho các ứng dụng hoàn toàn mới mà bạn thậm chí không có cơ sở dữ liệu. Bạn xác định các lớp và mã của mình rồi để Code First tìm ra cơ sở dữ liệu của bạn trông như thế nào.

  • Cũng có thể bắt đầu Code First với một cơ sở dữ liệu và điều đó làm cho Code First có chút mâu thuẫn. Nhưng có một công cụ cho phép bạn thiết kế ngược cơ sở dữ liệu thành các lớp, đây là một cách tuyệt vời để bắt đầu viết mã.

Với các tùy chọn này, chúng ta hãy nhìn vào Cây quyết định.

  • Nếu bạn thích làm việc với Trình thiết kế trực quan trong mã được tạo, thì bạn sẽ muốn chọn một trong các quy trình làm việc liên quan đến Trình thiết kế EF. Nếu cơ sở dữ liệu của bạn đã tồn tại, thì Database First là đường dẫn của bạn.

  • Nếu bạn muốn sử dụng Visual Designer trên một dự án hoàn toàn mới mà không có cơ sở dữ liệu, thì bạn sẽ muốn sử dụng Model First.

  • Nếu bạn chỉ muốn làm việc với mã chứ không phải một Nhà thiết kế, thì Code First có lẽ dành cho bạn cùng với tùy chọn sử dụng công cụ đảo ngược kỹ sư cơ sở dữ liệu thành các lớp.

  • Nếu bạn có các lớp học hiện có, thì tốt nhất bạn nên sử dụng chúng với Code First.

Trong các chương trước, bạn đã học ba cách khác nhau để xác định mô hình dữ liệu thực thể.

  • Hai trong số đó, Database First và Model First, phụ thuộc vào trình thiết kế Entity Framework kết hợp với việc tạo mã.

  • Phần thứ ba, Code First, cho phép bạn bỏ qua trình thiết kế trực quan và chỉ cần viết mã của riêng bạn.

  • Bất kể bạn chọn đường dẫn nào, bạn sẽ kết thúc với các lớp miền và một hoặc nhiều lớp DbContext của Entity Framework cho phép bạn truy xuất và lưu giữ dữ liệu liên quan đến các lớp đó.

API DbContext trong các ứng dụng của bạn được sử dụng làm cầu nối giữa các lớp và cơ sở dữ liệu của bạn. DbContext là một trong những lớp quan trọng nhất trong Entity Framework.

  • Nó cho phép thể hiện và thực hiện các truy vấn.

  • Nó lấy các kết quả truy vấn từ cơ sở dữ liệu và biến chúng thành các thể hiện của các lớp mô hình của chúng tôi.

  • Nó có thể theo dõi các thay đổi đối với các thực thể, bao gồm thêm và xóa, sau đó kích hoạt việc tạo các câu lệnh chèn, cập nhật và xóa được gửi đến cơ sở dữ liệu theo yêu cầu.

Sau đây là các lớp ngữ cảnh quảng cáo tên miền mà chúng tôi sẽ thực hiện các thao tác khác nhau trong chương này. Đây là ví dụ tương tự mà chúng tôi đã tạo trong chapater, Phương pháp tiếp cận cơ sở dữ liệu đầu tiên.

Triển khai lớp ngữ cảnh

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;

namespace DatabaseFirstDemo {

   public partial class UniContextEntities : DbContext {

      public UniContextEntities(): base("name = UniContextEntities") {}

      protected override void OnModelCreating(DbModelBuilder modelBuilder) {
         throw new UnintentionalCodeFirstException();
      }

      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }
}

Triển khai các lớp miền

Lớp học

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic;
	
   public partial class Course {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Course() {
         this.Enrollments = new HashSet<Enrollment>();
      }
	
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
	
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         "CA2227:CollectionPropertiesShouldBeReadOnly")]
			
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }
}

Lớp học sinh

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic; 

   public partial class Student {

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         "CA2214:DoNotCallOverridableMethodsInConstructors")]

      public Student() {
         this.Enrollments = new HashSet<Enrollment>();
      }

      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public System.DateTime EnrollmentDate { get; set; }

      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", 
         "CA2227:CollectionPropertiesShouldBeReadOnly")]
			
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }
}

Lớp tuyển sinh

namespace DatabaseFirstDemo {

   using System;
   using System.Collections.Generic; 

   public partial class Enrollment {

      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Nullable<int> Grade { get; set; }
		
      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }
}

Tạo hoạt động

Thêm một đối tượng mới với Entity Framework đơn giản như việc xây dựng một phiên bản mới của đối tượng của bạn và đăng ký nó bằng phương pháp Add trên DbSet. Đoạn mã sau cho phép bạn thêm một sinh viên mới vào cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      var newStudent = new Student();

      //set student name

      newStudent.FirstMidName = "Bill";
      newStudent.LastName = "Gates";
      newStudent.EnrollmentDate = DateTime.Parse("2015-10-21");
      newStudent.ID = 100;

      //create DBContext object

      using (var dbCtx = new UniContextEntities()) {

         //Add Student object into Students DBset
         dbCtx.Students.Add(newStudent);

         // call SaveChanges method to save student into database
         dbCtx.SaveChanges();
      }
   }
}

Cập nhật hoạt động

Thay đổi các đối tượng hiện có đơn giản như cập nhật giá trị được gán cho (các) thuộc tính bạn muốn thay đổi và gọi SaveChanges. Ví dụ, đoạn mã sau được sử dụng để thay đổi họ của Ali từ Khan thành Aslam.

using (var context = new UniContextEntities()) {

   var student = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
   student.LastName = "Aslam";
   context.SaveChanges();
}

Xóa hoạt động

Để xóa một thực thể bằng Entity Framework, bạn sử dụng phương pháp Remove trên DbSet. Xóa tác phẩm cho cả thực thể hiện có và thực thể mới được thêm vào. Gọi Xóa trên một thực thể đã được thêm nhưng chưa được lưu vào cơ sở dữ liệu sẽ hủy bỏ việc thêm đối tượng. Thực thể bị xóa khỏi trình theo dõi thay đổi và không còn được theo dõi bởi DbContext. Việc gọi Xóa trên một thực thể hiện có đang được theo dõi thay đổi sẽ đăng ký thực thể để xóa vào lần gọi SaveChanges tiếp theo. Ví dụ sau đây là mã nơi sinh viên bị xóa khỏi cơ sở dữ liệu có tên là Ali.

using (var context = new UniContextEntities()) {
   var bay = (from d in context.Students where d.FirstMidName == "Ali" select d).Single();
   context.Students.Remove(bay);
   context.SaveChanges();
}

Đọc hoạt động

Đọc dữ liệu hiện có từ cơ sở dữ liệu rất đơn giản. Sau đây là mã trong đó tất cả dữ liệu từ bảng Sinh viên được truy xuất và sau đó một chương trình sẽ được hiển thị với họ và tên của sinh viên theo thứ tự bảng chữ cái.

using (var db = new UniContextEntities()) {

   var query = from b in db.Students orderby b.FirstMidName select b;
   Console.WriteLine("All All student in the database:");

   foreach (var item in query) {
      Console.WriteLine(item.FirstMidName +" "+ item.LastName);
   }

   Console.WriteLine("Press any key to exit...");
   Console.ReadKey();
}

Bất kỳ nhà phát triển quyền truy cập dữ liệu nào cũng gặp khó khăn khi trả lời câu hỏi liên quan đến tính đồng thời của dữ liệu, "Điều gì sẽ xảy ra nếu nhiều người đang chỉnh sửa cùng một dữ liệu cùng một lúc?"

  • Những người may mắn hơn trong chúng ta đối phó với các quy tắc kinh doanh nói rằng "không có vấn đề, cuối cùng là chiến thắng."

  • Trong trường hợp này, đồng thời không phải là một vấn đề. Nhiều khả năng nó không đơn giản như vậy và không có viên đạn bạc nào để giải quyết mọi tình huống cùng một lúc.

  • Theo mặc định, Entity Framework sẽ sử dụng đường dẫn “người cuối cùng thắng”, nghĩa là bản cập nhật mới nhất được áp dụng ngay cả khi người khác cập nhật dữ liệu giữa thời gian dữ liệu được truy xuất và dữ liệu thời gian được lưu.

Hãy lấy một ví dụ để hiểu rõ hơn. Ví dụ sau thêm một cột mới VersionNo trong bảng Course.

Đi tới trình thiết kế và nhấp chuột phải vào cửa sổ trình thiết kế và chọn mô hình cập nhật từ cơ sở dữ liệu…

Bạn sẽ thấy rằng một cột khác được thêm vào Thực thể khóa học.

Nhấp chuột phải vào cột VersionNo mới tạo và chọn Properties và thay đổi ConcurrencyMode thành Fixed như trong hình sau.

Với ConcurrencyMode of Course.VersionNo được đặt thành Fixed, bất cứ khi nào một Khóa học được cập nhật, lệnh Cập nhật sẽ tìm kiếm Khóa học bằng cách sử dụng EntityKey và thuộc tính VersionNo của nó.

Hãy xem một kịch bản đơn giản. Hai người dùng truy xuất cùng một khóa học cùng lúc và người dùng 1 thay đổi tiêu đề của khóa học đó thành Toán học và lưu các thay đổi trước người dùng 2. Sau đó, khi người dùng 2 thay đổi tiêu đề của khóa học đó đã được truy xuất trước khi người dùng 1 lưu các thay đổi của mình, trong đó trường hợp người dùng 2 sẽ nhận được ngoại lệ đồng thời"User2: Optimistic Concurrency exception occured".

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         Course c1 = null;
         Course c2 = null;

         //User 1 gets Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 2 also get the same Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 1 updates Course Title
         c1.Title = "Edited from user1";

         //User 2 updates Course Title
         c2.Title = "Edited from user2";

         //User 1 saves changes first

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c1).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User1: Optimistic Concurrency exception occurred");
            }
         }

         //User 2 saves changes after User 1.
         //User 2 will get concurrency exection
         //because CreateOrModifiedDate is different in the database

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c2).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User2: Optimistic Concurrency exception occurred");
            }
         }
      }
   }
}

Trong tất cả các phiên bản của Entity Framework, bất cứ khi nào bạn thực thi SaveChanges()để chèn, cập nhật hoặc xóa cơ sở dữ liệu, khuôn khổ sẽ bao bọc hoạt động đó trong một giao dịch. Khi bạn gọi SaveChanges, ngữ cảnh sẽ tự động bắt đầu một giao dịch và cam kết hoặc khôi phục nó tùy thuộc vào việc duy trì có thành công hay không.

  • Tất cả điều này là minh bạch đối với bạn và bạn sẽ không bao giờ cần phải xử lý nó.

  • Giao dịch này chỉ kéo dài đủ lâu để thực hiện hoạt động và sau đó hoàn tất.

  • Khi bạn thực hiện một thao tác khác như vậy, một giao dịch mới sẽ bắt đầu.

Entity Framework 6 cung cấp những điều sau:

Database.BeginTransaction ()

  • Đây là một phương pháp đơn giản và dễ dàng hơn trong DbContext hiện có để bắt đầu và hoàn thành các giao dịch cho người dùng.

  • Nó cho phép một số hoạt động được kết hợp trong cùng một giao dịch và do đó tất cả đều được cam kết hoặc tất cả được thu hồi lại làm một.

  • Nó cũng cho phép người dùng dễ dàng chỉ định mức cô lập cho giao dịch.

Database.UseTransaction ()

  • Nó cho phép DbContext sử dụng một giao dịch, được bắt đầu bên ngoài Entity Framework.

Hãy xem xét ví dụ sau, trong đó nhiều thao tác được thực hiện trong một giao dịch. Mã là -

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         using (var dbContextTransaction = context.Database.BeginTransaction()) {

            try {

               Student student = new Student() {
                  ID = 200, 
                  FirstMidName = "Ali", 
                  LastName = "Khan", 
                  EnrollmentDate = DateTime.Parse("2015-12-1")
               };

               context.Students.Add(student);

               context.Database.ExecuteSqlCommand(@"UPDATE Course SET Title = 
                  'Calculus'" + "WHERE CourseID = 1045");

               var query = context.Courses.Where(c ⇒ c.CourseID == 1045);

               foreach (var item in query) {
                  Console.WriteLine(item.CourseID.ToString()
                     + " " + item.Title + " " + item.Credits);
               }

               context.SaveChanges();
               var query1 = context.Students.Where(s ⇒ s.ID == 200);

               foreach (var item in query1) {
                  Console.WriteLine(item.ID.ToString() 
                     + " " + item.FirstMidName + " " + item.LastName);
               }

               dbContextTransaction.Commit();
            } catch (Exception) {
               dbContextTransaction.Rollback();
            }

         }
      }
   }
}
  • Bắt đầu giao dịch yêu cầu kết nối cửa hàng cơ bản phải mở.

  • Vì vậy, việc gọi Database.BeginTransaction () sẽ mở kết nối, nếu nó chưa được mở.

  • Nếu DbContextTransaction đã mở kết nối thì nó sẽ đóng nó khi Dispose () được gọi.

Chế độ xem là một đối tượng có chứa dữ liệu thu được bởi một truy vấn xác định trước. Chế độ xem là một đối tượng hoặc bảng ảo có tập kết quả bắt nguồn từ một truy vấn. Nó rất giống với một bảng thực vì nó chứa các cột và hàng dữ liệu. Sau đây là một số cách sử dụng điển hình của khung nhìn -

  • Lọc dữ liệu của các bảng bên dưới
  • Lọc dữ liệu cho mục đích bảo mật
  • Tập trung dữ liệu được phân phối trên một số máy chủ
  • Tạo một tập hợp dữ liệu có thể tái sử dụng

Chế độ xem có thể được sử dụng theo cách tương tự như bạn có thể sử dụng bảng. Để sử dụng chế độ xem như một thực thể, trước tiên, bạn sẽ cần thêm các chế độ xem cơ sở dữ liệu vào EDM. Sau khi thêm các dạng xem vào mô hình của mình, bạn có thể làm việc với nó theo cách giống như các thực thể bình thường ngoại trừ các thao tác Tạo, Cập nhật và Xóa.

Hãy cùng xem, làm thế nào để thêm các khung nhìn vào mô hình từ cơ sở dữ liệu.

Step 1 - Tạo một dự án Ứng dụng Console mới.

Step 2 - Nhấp chuột phải vào dự án trong trình khám phá giải pháp và chọn Thêm → Mục mới.

Step 3 - Chọn ADO.NET Entity Data Model từ ngăn giữa và nhập tên ViewModel vào trường Name.

Step 4 - Nhấp vào nút Thêm sẽ khởi chạy hộp thoại Trình hướng dẫn mô hình dữ liệu thực thể.

Step 5 - Chọn EF Designer từ cơ sở dữ liệu và nhấp vào nút Tiếp theo.

Step 6 - Chọn cơ sở dữ liệu hiện có và nhấp vào Tiếp theo.

Step 7 - Chọn Entity Framework 6.x và nhấp vào Tiếp theo.

Step 8 - Chọn bảng và dạng xem từ cơ sở dữ liệu của bạn và nhấp vào Kết thúc.

Bạn có thể thấy trong cửa sổ trình thiết kế rằng một dạng xem đã được tạo và bạn có thể sử dụng nó trong chương trình như một thực thể.

Trong trình khám phá giải pháp, bạn có thể thấy rằng lớp MyView cũng được tạo từ cơ sở dữ liệu.

Hãy lấy một ví dụ trong đó tất cả dữ liệu được truy xuất từ ​​chế độ xem. Sau đây là mã -

class Program {

   static void Main(string[] args) {

      using (var db = new UniContextEntities()) {

         var query = from b in db.MyViews
            orderby b.FirstMidName select b;

         Console.WriteLine("All student in the database:");

         foreach (var item in query) {
            Console.WriteLine(item.FirstMidName + " " + item.LastName);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

All student in the database:
Ali Khan
Arturo   finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Chỉ mục là một cấu trúc dữ liệu trên đĩa dựa trên các bảng và dạng xem. Các chỉ mục giúp cho việc truy xuất dữ liệu nhanh hơn và hiệu quả, trong hầu hết các trường hợp. Tuy nhiên, việc quá tải một bảng hoặc dạng xem với các chỉ mục có thể ảnh hưởng đến hiệu suất của các hoạt động khác như chèn hoặc cập nhật.

  • Lập chỉ mục là tính năng mới trong khung thực thể, nơi bạn có thể cải thiện hiệu suất của ứng dụng Code First của mình bằng cách giảm thời gian cần thiết để truy vấn dữ liệu từ cơ sở dữ liệu.

  • Bạn có thể thêm chỉ mục vào cơ sở dữ liệu của mình bằng cách sử dụng Index thuộc tính và ghi đè mặc định UniqueClustered cài đặt để có được chỉ mục phù hợp nhất với tình huống của bạn.

Hãy xem đoạn mã sau, trong đó thuộc tính Index được thêm vào lớp Course cho CourseID.

public partial class Course {

   public Course() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   [Index]
   public int CourseID { get; set; }
   public string Title { get; set; }
	
   public int Credits { get; set; }
   public byte[] VersionNo { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Khóa được tạo ở trên là không duy nhất, không nhóm. Có quá tải có sẵn để ghi đè các giá trị mặc định này -

  • Để tạo chỉ mục thành chỉ mục Clustered, bạn cần chỉ định IsClustered = true

  • Tương tự, bạn cũng có thể đặt một chỉ mục thành một chỉ mục duy nhất bằng cách chỉ định IsUnique = true

Hãy xem đoạn mã C # sau đây, nơi một chỉ mục được nhóm và duy nhất.

public partial class Course {

   public Course() {
      this.Enrollments = new HashSet<Enrollment>();
   }

   [Index(IsClustered = true, IsUnique = true)]
   public int CourseID { get; set; }
   public string Title { get; set; }
	
   public int Credits { get; set; }
   public byte[] VersionNo { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Thuộc tính Index có thể được sử dụng để tạo một chỉ mục duy nhất trong cơ sở dữ liệu. Tuy nhiên, điều này không có nghĩa là EF có thể suy luận về tính duy nhất của cột khi xử lý các mối quan hệ, v.v. Tính năng này thường được gọi là hỗ trợ cho “các ràng buộc duy nhất”.

Khung thực thể cho phép bạn sử dụng các thủ tục được lưu trữ trong Mô hình dữ liệu thực thể thay vì hoặc kết hợp với việc tạo lệnh tự động của nó.

  • Bạn có thể sử dụng các thủ tục được lưu trữ để thực hiện logic được xác định trước trên các bảng cơ sở dữ liệu và nhiều tổ chức có các chính sách yêu cầu sử dụng các thủ tục được lưu trữ này.

  • Nó cũng có thể chỉ định rằng EF nên sử dụng các thủ tục được lưu trữ của bạn để chèn, cập nhật hoặc xóa các thực thể.

  • Mặc dù các lệnh được xây dựng động là an toàn, hiệu quả và nói chung là tốt hoặc tốt hơn các lệnh bạn có thể tự viết, nhưng có nhiều trường hợp các thủ tục được lưu trữ đã tồn tại và các thông lệ của công ty bạn có thể hạn chế việc sử dụng trực tiếp các bảng.

  • Ngoài ra, bạn có thể chỉ muốn có quyền kiểm soát rõ ràng đối với những gì được thực thi trên cửa hàng và muốn tạo các thủ tục được lưu trữ.

Ví dụ sau tạo một dự án mới từ Tệp → Mới → Dự án.

Step 1 - Chọn Ứng dụng Bảng điều khiển từ ngăn giữa và nhập StoredProceduresDemo vào trường tên.

Step 2 - Trong Trình thám hiểm máy chủ, nhấp chuột phải vào cơ sở dữ liệu của bạn.

Step 3 - Chọn Truy vấn Mới và nhập mã sau vào trình soạn thảo T-SQL để thêm một bảng mới vào cơ sở dữ liệu của bạn.

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = 
   OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))

BEGIN

   CREATE TABLE [dbo].[StudentGrade](

      [EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
      [CourseID] [int] NOT NULL,
      [StudentID] [int] NOT NULL,
      [Grade] [decimal](3, 2) NULL,

      CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED (
         [EnrollmentID] ASC
      )

      WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

   ) ON [PRIMARY]

END
GO

Step 4 - Nhấp chuột phải vào trình soạn thảo và chọn Execute.

Step 5- Nhấp chuột phải vào cơ sở dữ liệu của bạn và nhấp vào làm mới. Bạn sẽ thấy bảng mới được thêm vào cơ sở dữ liệu của mình.

Step 6 - Trong Trình khám phá máy chủ, nhấp chuột phải vào cơ sở dữ liệu của bạn một lần nữa.

Step 7 - Chọn Truy vấn Mới và nhập mã sau vào trình soạn thảo T-SQL để thêm một thủ tục được lưu trữ trong cơ sở dữ liệu của bạn, thủ tục này sẽ trả về điểm của Học sinh.

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = 
   OBJECT_ID(N'[dbo].[GetStudentGrades]') AND type in (N'P', N'PC'))

BEGIN

   EXEC dbo.sp_executesql @statement = N'
   CREATE PROCEDURE [dbo].[GetStudentGrades]
   @StudentID int
   AS
   SELECT EnrollmentID, Grade, CourseID, StudentID FROM dbo.StudentGrade 
   WHERE StudentID = @StudentID
   '
END
GO

Step 8 - Nhấp chuột phải vào trình soạn thảo và chọn Execute.

Step 9- Nhấp chuột phải vào cơ sở dữ liệu của bạn và nhấp vào làm mới. Bạn sẽ thấy rằng một thủ tục được lưu trữ được tạo trong cơ sở dữ liệu của bạn.

Step 10 - Nhấp chuột phải vào tên dự án trong Solution Explorer và chọn Add → New Item.

Step 11 - Sau đó chọn ADO.NET Entity Data Model trong khung Templates.

Step 12 - Nhập SPModel làm tên, sau đó nhấp vào Thêm.

Step 13 - Trong hộp thoại Chọn Nội dung Mô hình, chọn trình thiết kế EF từ cơ sở dữ liệu, rồi bấm Tiếp theo.

Step 14 - Chọn cơ sở dữ liệu của bạn và nhấp vào Tiếp theo.

Step 15 - Trong hộp thoại Chọn Đối tượng Cơ sở dữ liệu của Bạn, bấm vào các bảng, dạng xem.

Step 16 - Chọn chức năng GetStudentGradesForCourse nằm dưới nút Thủ tục và Chức năng đã Lưu trữ và nhấp vào Kết thúc.

Step 17 - Chọn Chế độ xem → Windows khác → Trình duyệt mô hình dữ liệu thực thể và nhấp chuột phải vào GetStudentGrades trong Nhập chức năng và chọn Chỉnh sửa.

Nó sẽ tạo ra hộp thoại sau.

Step 18 - Nhấp vào nút radio Entities và chọn StudentGrade từ hộp kết hợp làm loại trả lại của thủ tục được lưu trữ này và nhấp vào Ok.

Chúng ta hãy xem đoạn mã C # sau, trong đó tất cả các điểm sẽ được truy xuất bằng cách chuyển ID sinh viên làm tham số trong thủ tục được lưu trữ GetStudentGrades.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         int studentID = 22;
         var studentGrades = context.GetStudentGrades(studentID);

         foreach (var student in studentGrades) {
            Console.WriteLine("Course ID: {0}, Title: {1}, Grade: {2} ", 
               student.CourseID, student.Course.Title, student.Grade);
         }

         Console.ReadKey();

      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau:

Course ID: 4022, Title: Microeconomics, Grade: 3.00
Course ID: 4041, Title: Macroeconomics, Grade: 3.50

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong chương này, chúng ta hãy xem cách thực hiện thay đổi đối với các thực thể không được theo dõi bởi ngữ cảnh. Các thực thể không được ngữ cảnh theo dõi được gọi là các thực thể 'ngắt kết nối'.

  • Đối với hầu hết các ứng dụng một lớp, nơi giao diện người dùng và các lớp truy cập cơ sở dữ liệu chạy trong cùng một quy trình ứng dụng, bạn có thể sẽ chỉ thực hiện các thao tác trên các thực thể đang được theo dõi bởi ngữ cảnh.

  • Các hoạt động trên các thực thể bị ngắt kết nối phổ biến hơn nhiều trong các ứng dụng N-Tier.

  • Ứng dụng N-Tier liên quan đến việc tìm nạp một số dữ liệu trên máy chủ và trả lại dữ liệu đó qua mạng cho một máy khách.

  • Sau đó, ứng dụng khách sẽ thao tác dữ liệu này trước khi đưa nó trở lại máy chủ.

Sau đây là hai bước cần được thực hiện với biểu đồ thực thể bị ngắt kết nối hoặc thậm chí một thực thể bị ngắt kết nối.

  • Đính kèm các thực thể với phiên bản ngữ cảnh mới và làm cho ngữ cảnh nhận biết về các thực thể này.

  • Đặt các Thực thể thích hợp cho các thực thể này theo cách thủ công.

Hãy xem đoạn mã sau, trong đó thực thể Sinh viên được thêm vào với hai thực thể Ghi danh.

class Program {

   static void Main(string[] args) {

      var student = new Student {

         ID = 1001,
         FirstMidName = "Wasim",
         LastName = "Akram", 

         EnrollmentDate = DateTime.Parse("2015-10-10"), 
            Enrollments = new List<Enrollment> {

               new Enrollment{EnrollmentID = 2001,CourseID = 4022, StudentID = 1001 },
               new Enrollment{EnrollmentID = 2002,CourseID = 4025, StudentID = 1001 },
         }
      };

      using (var context = new UniContextEntities()) {

         context.Students.Add(student);
         Console.WriteLine("New Student ({0} {1}): {2}", 
            student.FirstMidName, student.LastName, context.Entry(student).State);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0} State: {1}", 
               enrollment.EnrollmentID, context.Entry(enrollment).State);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   } 
}
  • Mã xây dựng một cá thể Sinh viên mới, nó cũng tham chiếu đến hai cá thể Ghi danh mới trong thuộc tính Đăng ký của nó.

  • Sau đó, Sinh viên mới được thêm vào ngữ cảnh bằng phương pháp Thêm.

  • Sau khi Sinh viên được thêm, mã sử dụng phương thức DbContext.Entry để có quyền truy cập vào thông tin theo dõi thay đổi mà Entity Framework có về Sinh viên mới.

  • Từ thông tin theo dõi thay đổi này, thuộc tính State được sử dụng để ghi ra trạng thái hiện tại của thực thể.

  • Sau đó, quá trình này được lặp lại cho từng Đăng ký mới được tạo được tham chiếu từ Học sinh mới. Nếu bạn chạy ứng dụng, bạn sẽ nhận được kết quả sau:

New Student   (Wasim  Akram): Added
Enrollment ID: 2001 State: Added
Enrollment ID: 2002 State: Added
Press any key to exit...

Trong khi DbSet.Add được sử dụng để thông báo cho Entity Framework về các thực thể mới, thì DbSet.Attach được sử dụng để thông báo cho Entity Framework về các thực thể hiện có. Phương thức Đính kèm sẽ đánh dấu một thực thể ở trạng thái Không thay đổi.

Hãy xem đoạn mã C # sau, trong đó một thực thể bị ngắt kết nối được đính kèm với DbContext.

class Program {

   static void Main(string[] args) {

      var student = new Student {

         ID = 1001,
         FirstMidName = "Wasim",
         LastName = "Akram",
         EnrollmentDate = DateTime.Parse("2015-10-10"), 

         Enrollments = new List<Enrollment> {
            new Enrollment { EnrollmentID = 2001, CourseID = 4022, StudentID = 1001 },
            new Enrollment { EnrollmentID = 2002, CourseID = 4025, StudentID = 1001 },
         }
			
      };

      using (var context = new UniContextEntities()) {

         context.Students.Attach(student);
         Console.WriteLine("New Student ({0} {1}): {2}", 
            student.FirstMidName, student.LastName, context.Entry(student).State);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0} State: {1}", enrollment.EnrollmentID, 
               context.Entry(enrollment).State);
         }

         Console.WriteLine("Press any key to exit...");
         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được thực thi bằng phương thức Attach (), bạn sẽ nhận được kết quả sau.

New Student   (Wasim  Akram): Unchanged
Enrollment ID: 2001 State: Unchanged
Enrollment ID: 2002 State: Unchanged
Press any key to exit...

Trong chương này, chúng ta hãy tìm hiểu cách ánh xạ các Hàm có giá trị trong bảng (TVF) bằng cách sử dụng Trình thiết kế khung thực thể và cách gọi một TVF từ truy vấn LINQ.

  • TVF hiện chỉ được hỗ trợ trong dòng công việc Cơ sở dữ liệu đầu tiên.

  • Nó được giới thiệu lần đầu tiên trong Entity Framework phiên bản 5.

  • Để sử dụng TVF, bạn phải nhắm mục tiêu .NET Framework 4.5 trở lên.

  • Nó rất giống với các thủ tục được lưu trữ nhưng có một điểm khác biệt chính, tức là, kết quả của TVF là có thể tổng hợp được. Điều này có nghĩa là kết quả từ TVF có thể được sử dụng trong một truy vấn LINQ trong khi kết quả của một thủ tục được lưu trữ thì không thể.

Hãy xem ví dụ sau về việc tạo một dự án mới từ Tệp → Mới → Dự án.

Step 1 - Chọn Ứng dụng Bảng điều khiển từ ngăn giữa và nhập TableValuedFunctionDemo vào trường tên.

Step 2 - Trong Trình thám hiểm máy chủ, nhấp chuột phải vào cơ sở dữ liệu của bạn.

Step 3 - Chọn Truy vấn Mới và nhập mã sau vào trình soạn thảo T-SQL để thêm một bảng mới vào cơ sở dữ liệu của bạn.

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id =
   OBJECT_ID(N'[dbo].[StudentGrade]') AND type in (N'U'))

BEGIN

   CREATE TABLE [dbo].[StudentGrade](

      [EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
      [CourseID] [int] NOT NULL,
      [StudentID] [int] NOT NULL,
      [Grade] [decimal](3, 2) NULL,

      CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED ([EnrollmentID] ASC)

      WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

   ) ON [PRIMARY]

END
GO

Step 4 - Nhấp chuột phải vào trình soạn thảo và chọn Execute.

Step 5- Nhấp chuột phải vào cơ sở dữ liệu của bạn và nhấp vào làm mới. Bạn sẽ thấy bảng mới được thêm vào cơ sở dữ liệu của mình.

Step 6- Bây giờ tạo một hàm sẽ trả về điểm của học sinh cho khóa học. Nhập mã sau vào trình soạn thảo T-SQL.

CREATE FUNCTION [dbo].[GetStudentGradesForCourse]

(@CourseID INT)

RETURNS TABLE

RETURN
   SELECT [EnrollmentID],
      [CourseID],
      [StudentID],
      [Grade]
   FROM   [dbo].[StudentGrade]
   WHERE  CourseID = @CourseID

Step 7 - Nhấp chuột phải vào trình soạn thảo và chọn Execute.

Bây giờ bạn có thể thấy rằng hàm đã được tạo.

Step 8 - Nhấp chuột phải vào tên dự án trong Solution Explorer và chọn Add → New Item.

Step 9 - Sau đó chọn ADO.NET Entity Data Model trong khung Templates.

Step 10 - Nhập TVFModel làm tên, sau đó nhấp vào Thêm.

Step 11 - Trong hộp thoại Chọn Nội dung Mô hình, chọn trình thiết kế EF từ cơ sở dữ liệu, rồi bấm Tiếp theo.

Step 12 - Chọn cơ sở dữ liệu của bạn và nhấp vào Tiếp theo.

Step 13 - Trong hộp thoại Chọn Đối tượng Cơ sở dữ liệu của Bạn chọn bảng, dạng xem.

Step 14 - Chọn chức năng GetStudentGradesForCourse nằm dưới nút Thủ tục và Chức năng đã Lưu trữ và nhấp vào Kết thúc.

Step 15 - Chọn Chế độ xem → Windows khác → Trình duyệt mô hình dữ liệu thực thể và nhấp chuột phải vào GetStudentGradesForCourse bên dưới Nhập hàm và chọn Chỉnh sửa.

Bạn sẽ thấy hộp thoại sau.

Step 16 - Nhấp vào nút radio Entities và chọn Enrollment từ combobox làm loại trả về của Chức năng này và nhấp Ok.

Hãy xem mã C # sau đây trong đó tất cả các điểm của sinh viên sẽ được truy xuất những người đã đăng ký vào Course ID = 4022 trong cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var CourseID = 4022;

         // Return all the best students in the Microeconomics class.
         var students = context.GetStudentGradesForCourse(CourseID);

         foreach (var result in students) {
            Console.WriteLine("Student ID:  {0}, Grade: {1}",
               result.StudentID, result.Grade);
         }

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau:

Student ID: 1, Grade: 2
Student ID: 4, Grade: 4
Student ID: 9, Grade: 3.5

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong Khung thực thể, bạn có thể truy vấn với các lớp thực thể của mình bằng LINQ. Bạn cũng có thể chạy các truy vấn bằng SQL thô trực tiếp đối với cơ sở dữ liệu bằng DbCOntext. Các kỹ thuật này có thể được áp dụng như nhau cho các mô hình được tạo bằng Code First và EF Designer.

Truy vấn SQL trên thực thể hiện có

Phương thức SqlQuery trên DbSet cho phép viết một truy vấn SQL thô sẽ trả về các cá thể thực thể. Các đối tượng trả về sẽ được theo dõi bởi ngữ cảnh giống như chúng sẽ được theo dõi nếu chúng được trả về bởi một truy vấn LINQ. Ví dụ -

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var students = context.Students.SqlQuery("SELECT * FROM dbo.Student").ToList();

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ",
               student.ID, name, student.EnrollmentDate.ToString());
         }

         Console.ReadKey();
      }
   }
}

Đoạn mã trên sẽ lấy tất cả học sinh từ cơ sở dữ liệu.

Truy vấn SQL cho các loại không phải thực thể

Một truy vấn SQL trả về các thể hiện của bất kỳ kiểu nào, bao gồm cả kiểu nguyên thủy, có thể được tạo bằng phương thức SqlQuery trên lớp Cơ sở dữ liệu. Ví dụ -

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         var studentNames = context.Database.SqlQuery
            <string>("SELECT FirstMidName FROM dbo.Student").ToList();

         foreach (var student in studentNames) {
            Console.WriteLine("Name: {0}", student);
         }

         Console.ReadKey();
      }
   }
}

Lệnh SQL cho cơ sở dữ liệu

Phương thức ExecuteSqlCommnad được sử dụng để gửi các lệnh không truy vấn đến cơ sở dữ liệu, chẳng hạn như lệnh Chèn, Cập nhật hoặc Xóa. Hãy xem đoạn mã sau, trong đó tên của học sinh được cập nhật là ID = 1

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         //Update command

         int noOfRowUpdated = context.Database.ExecuteSqlCommand("Update 
            student set FirstMidName = 'Ali' where ID = 1");

         context.SaveChanges();

         var student = context.Students.SqlQuery("SELECT * FROM
            dbo.Student where ID = 1").Single();

         string name = student.FirstMidName + " " + student.LastName;

         Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ", 
            student.ID, name, student.EnrollmentDate.ToString());

         Console.ReadKey();
      }
   }
}

Đoạn mã trên sẽ lấy tất cả tên của học sinh từ cơ sở dữ liệu.

Trong Entity Framework, tính năng này sẽ cho phép bạn xác định một thuộc tính trên một lớp miền là một kiểu enum và ánh xạ nó tới một cột cơ sở dữ liệu kiểu số nguyên. Entity Framework sau đó sẽ chuyển đổi giá trị cơ sở dữ liệu sang và từ enum có liên quan khi nó truy vấn và lưu dữ liệu.

  • Các kiểu được liệt kê có tất cả các loại lợi ích khi làm việc với các thuộc tính có số lượng phản hồi cố định.

  • Tính bảo mật và độ tin cậy của ứng dụng đều tăng lên khi bạn sử dụng kiểu liệt kê.

  • Việc liệt kê làm cho người dùng khó mắc lỗi hơn nhiều và các vấn đề như tấn công tiêm là không tồn tại.

  • Trong Khung thực thể, một kiểu liệt kê có thể có các kiểu cơ bản sau:

    • Byte
    • Int16
    • Int32
    • Int64
    • SByte
  • Kiểu cơ bản mặc định của các phần tử liệt kê là int.

  • Theo mặc định, điều tra viên đầu tiên có giá trị 0 và giá trị của mỗi điều tra viên kế tiếp được tăng lên 1.

Hãy xem ví dụ sau, trong đó chúng ta sẽ tạo một thực thể trong trình thiết kế và sau đó sẽ thêm một số thuộc tính.

Step 1 - Tạo dự án mới từ tùy chọn menu File → New → Project.

Step 2 - Trong ngăn bên trái, chọn Ứng dụng bảng điều khiển.

Step 3 - Nhập EFEnumDemo làm tên của dự án và nhấn OK.

Step 4 - Nhấp chuột phải vào tên dự án trong Solution Explorer và chọn tùy chọn menu Add → New Item.

Step 5 - Chọn ADO.NET Entity Data Model trong ngăn Templates.

Step 6 - Nhập EFEnumModel.edmx cho tên tệp, sau đó bấm Thêm.

Step 7 - Trên trang Entity Data Model Wizard, chọn Empty EF designer Model.

Step 8 - Nhấp vào Kết thúc

Step 9 - Sau đó nhấp chuột phải vào cửa sổ trình thiết kế và chọn Thêm → Thực thể.

Hộp thoại Thực thể Mới xuất hiện như thể hiện trong hình sau.

Step 10 - Nhập Phòng làm tên Thực thể và DeptID làm tên thuộc tính, để loại Thuộc tính là Int32 và nhấp OK.

Step 11 - Nhấp chuột phải vào thực thể và chọn Thêm mới → Thuộc tính vô hướng.

Step 12 - Đổi tên thuộc tính mới thành DeptName.

Step 13 - Thay đổi kiểu của thuộc tính mới thành Int32 (theo mặc định, thuộc tính mới là kiểu Chuỗi).

Step 14 - Để thay đổi kiểu, mở cửa sổ Thuộc tính và thay đổi thuộc tính Kiểu thành Int32.

Step 15 - Trong Entity Framework Designer, nhấp chuột phải vào thuộc tính Name, chọn Convert to enum.

Step 16 - Trong hộp thoại Thêm Loại Enum, nhập Tên loại Enum, thay đổi Loại Cơ bản thành Int32, sau đó thêm các thành viên sau vào loại: Vật lý, Hóa học, Máy tính và Kinh tế.

Step 17 - Bấm Ok.

Nếu bạn chuyển sang cửa sổ Trình duyệt Mô hình, bạn sẽ thấy rằng kiểu cũng đã được thêm vào nút Kiểu Enum.

Hãy tạo cơ sở dữ liệu từ mô hình bằng cách làm theo tất cả các bước được đề cập trong chương tiếp cận Mô hình thứ nhất.

Step 1 - Nhấp chuột phải vào bề mặt Entity Designer và chọn Generate Database from Model.

Hộp thoại Chọn Kết nối Dữ liệu của Bạn của Trình hướng dẫn Tạo Cơ sở dữ liệu được hiển thị.

Step 2 - Nhấp vào nút Kết nối Mới.

Step 3 - Nhập tên máy chủ và EnumDemo cho cơ sở dữ liệu và nhấn OK.

Step 4 - Một hộp thoại hỏi bạn có muốn tạo cơ sở dữ liệu mới không sẽ bật lên, nhấn Yes.

Step 5- Nhấp vào Tiếp theo và Trình hướng dẫn Tạo Cơ sở dữ liệu tạo ngôn ngữ định nghĩa dữ liệu (DDL) để tạo cơ sở dữ liệu. Bây giờ hãy nhấp vào Kết thúc.

Step 6 - Nhấp chuột phải vào T-SQL Editor và chọn Execute.

Step 7 - Để xem lược đồ đã tạo, nhấp chuột phải vào tên cơ sở dữ liệu trong SQL Server Object Explorer và chọn Refresh.

Bạn sẽ thấy bảng Phòng ban trong cơ sở dữ liệu.

Chúng ta hãy xem ví dụ sau, trong đó một số đối tượng Bộ mới trong ngữ cảnh được thêm và lưu. Và sau đó truy xuất bộ phận Máy tính.

class Program {

   static void Main(string[] args) {

      using (var context = new EFEnumModelContainer()) {

         context.Departments.Add(new Department { DeptName = DepartmentNames.Physics});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Computer});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Chemistry});
         context.Departments.Add(new Department { DeptName = DepartmentNames.Economics});

         context.SaveChanges();

         var department = (
            from d in context.Departments
            where d.DeptName == DepartmentNames.Computer
            select d
         ).FirstOrDefault();

         Console.WriteLine(
            "Department ID: {0}, Department Name: {1}", 
               department.DeptID, department.DeptName
         );

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

Department ID: 2, Department Name: Computer

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Asynchronous programmingliên quan đến việc thực thi các hoạt động trong nền để luồng chính có thể tiếp tục các hoạt động của chính nó. Bằng cách này, luồng chính có thể giữ cho giao diện người dùng phản hồi trong khi luồng nền đang xử lý tác vụ trong tầm tay.

  • Entity Framework 6.0 hỗ trợ các hoạt động không đồng bộ để truy vấn và lưu dữ liệu.

  • Các hoạt động không đồng bộ có thể giúp ứng dụng của bạn theo những cách sau:

    • Làm cho ứng dụng của bạn phản hồi nhanh hơn với các tương tác của người dùng
    • Cải thiện hiệu suất tổng thể của ứng dụng của bạn
  • Bạn có thể thực hiện các hoạt động không đồng bộ theo nhiều cách khác nhau. Nhưng các từ khóa async / await đã được giới thiệu trong .NET Framework 4.5 giúp công việc của bạn trở nên đơn giản.

  • Điều duy nhất bạn cần làm theo là mẫu async / await như được minh họa bằng đoạn mã sau.

Hãy xem ví dụ sau (không sử dụng async / await) trong đó phương thức DatabaseOperations lưu một học sinh mới vào cơ sở dữ liệu và sau đó truy xuất tất cả học sinh từ cơ sở dữ liệu và cuối cùng một số thông báo bổ sung được in trên bảng điều khiển.

class Program {

   static void Main(string[] args) {
      Console.WriteLine("Database Operations Started");
      DatabaseOperations();
		
      Console.WriteLine();
      Console.WriteLine("Database Operations Completed");
      Console.WriteLine();
      Console.WriteLine("Entity Framework Tutorials");
      Console.ReadKey();
   }

   public static void DatabaseOperations() {

      using (var context = new UniContextEntities()) {

         // Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Akram", 
            LastName = "Khan", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});

         Console.WriteLine("Calling SaveChanges.");
         context.SaveChanges();
         Console.WriteLine("SaveChanges completed.");

         // Query for all Students ordered by first name

         var students = (from s in context.Students
            orderby s.FirstMidName select s).ToList();

         // Write all students out to Console

         Console.WriteLine();
         Console.WriteLine("All Student:");

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine(" " + name);
         }
      }
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

Calling SaveChanges.
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura  Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Yan Li

Entity Framework Tutorials

Hãy sử dụng từ khóa async và await mới và thực hiện các thay đổi sau đối với Program.cs

  • Thêm không gian tên System.Data.Entity sẽ cung cấp các phương thức mở rộng không đồng bộ EF.

  • Thêm không gian tên System.Threading.Tasks sẽ cho phép chúng tôi sử dụng loại Tác vụ.

  • Cập nhật DatabaseOperations được đánh dấu là async và trả lại một Task.

  • Gọi phiên bản Async của SaveChanges và chờ hoàn thành.

  • Gọi phiên bản Async của ToList và chờ đợi kết quả.

class Program {

   static void Main(string[] args) {
      var task = DatabaseOperations();
      Console.WriteLine();
      Console.WriteLine("Entity Framework Tutorials");
      task.Wait();
      Console.ReadKey();
   }

   public static async Task DatabaseOperations() {

      using (var context = new UniContextEntities()) {

         // Create a new blog and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman", 
            LastName = "Khan", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())});

         Console.WriteLine("Calling SaveChanges.");
         await context.SaveChangesAsync();
         Console.WriteLine("SaveChanges completed.");

         // Query for all Students ordered by first name

         var students = await (from s in context.Students 
            orderby s.FirstMidName select s).ToListAsync();

         // Write all students out to Console

         Console.WriteLine();
         Console.WriteLine("All Student:");

         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName; 
            Console.WriteLine(" " + name);
         }
      }
   }
}

Khi thực thi, nó sẽ tạo ra kết quả sau.

Calling SaveChanges.
Entity Framework Tutorials
SaveChanges completed.
All Student:
Akram Khan
Ali Khan
Ali Alexander
Arturo Anand
Bill Gates
Gytis Barzdukas
Laura  Nornan
Meredith fllonso
Nino Olioetto
Peggy Justice
Salman Khan
Yan Li

Bây giờ mã không đồng bộ, bạn có thể quan sát luồng thực thi khác của chương trình của bạn.

  • SaveChanges bắt đầu đẩy Sinh viên mới vào cơ sở dữ liệu và sau đó phương thức DatabaseOperations trả về (ngay cả khi nó chưa thực thi xong) và luồng chương trình trong phương thức Chính tiếp tục.

  • Sau đó tin nhắn được ghi vào bảng điều khiển.

  • Luồng được quản lý bị chặn trên cuộc gọi Chờ cho đến khi hoạt động cơ sở dữ liệu hoàn tất. Khi nó hoàn tất, phần còn lại của DatabaseOperations của chúng tôi sẽ được thực thi.

  • SaveChanges hoàn tất.

  • Lấy tất cả học sinh từ cơ sở dữ liệu và được ghi vào Bảng điều khiển.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Khung thực thể hiện cho phép bạn hưởng lợi từ Khung thực thể mà không buộc mọi phần trong ứng dụng của bạn phải biết về Khung thực thể, tách các thực thể khỏi cơ sở hạ tầng. Bạn có thể tạo các lớp có thể tập trung vào các quy tắc kinh doanh của chúng mà không cần quan tâm đến việc chúng được tồn tại như thế nào (nơi dữ liệu được lưu trữ và cách dữ liệu qua lại giữa các đối tượng của bạn).

Tạo đối tượng thiếu hiểu biết liên tục

Đoạn trước đã mô tả một phương pháp không có kiến ​​thức sâu sắc về nguồn dữ liệu mà nó sử dụng. Điều này làm nổi bật bản chất của sự thiếu hiểu biết về tính bền bỉ, đó là khi các lớp của bạn và nhiều lớp ứng dụng của chúng tôi xung quanh chúng không quan tâm đến cách dữ liệu được lưu trữ.

  • Trong phiên bản .NET 3.5 của Entity Framework, nếu bạn muốn sử dụng các lớp có sẵn, bạn bắt buộc phải sửa đổi chúng bằng cách buộc chúng phải dẫn xuất từ ​​EntityObject.

  • Trong .NET 4, điều này không còn cần thiết nữa. Bạn không phải sửa đổi các thực thể của mình để chúng tham gia vào các hoạt động của Khung thực thể.

  • Điều này cho phép chúng tôi xây dựng các ứng dụng bao gồm khớp nối lỏng lẻo và tách biệt các mối quan tâm.

  • Với các mẫu mã này, các lớp của bạn chỉ quan tâm đến công việc của chính chúng và nhiều lớp ứng dụng của bạn, bao gồm cả giao diện người dùng, không có sự phụ thuộc vào logic bên ngoài, chẳng hạn như các API khung thực thể, nhưng các API bên ngoài đó có thể tương tác với các thực thể.

Có 2 cách (kết nối và ngắt kết nối) khi duy trì một thực thể với Khung thực thể. Cả hai cách đều có tầm quan trọng riêng. Trong trường hợp kịch bản được kết nối, các thay đổi được theo dõi bởi ngữ cảnh nhưng trong trường hợp kịch bản bị ngắt kết nối, chúng ta cần thông báo cho ngữ cảnh về trạng thái của thực thể.

Các tình huống được kết nối

Kịch bản được kết nối là khi một thực thể được truy xuất từ ​​cơ sở dữ liệu và được sửa đổi trong cùng một ngữ cảnh. Đối với tình huống được kết nối, hãy giả sử chúng ta có một dịch vụ Windows và chúng ta đang thực hiện một số hoạt động kinh doanh với thực thể đó, vì vậy chúng tôi sẽ mở ngữ cảnh, lặp qua tất cả các thực thể, thực hiện các hoạt động kinh doanh của chúng tôi và sau đó lưu các thay đổi với cùng ngữ cảnh mà chúng tôi mở đầu.

Hãy xem ví dụ sau, trong đó học sinh được truy xuất từ ​​cơ sở dữ liệu và cập nhật tên của học sinh, sau đó lưu các thay đổi vào cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      using (var context = new MyContext()) {

         var studentList = context.Students.ToList();

         foreach (var stdnt in studentList) {
            stdnt.FirstMidName = "Edited " + stdnt.FirstMidName;
         }

         context.SaveChanges();

         //// Display all Students from the database

         var students = (from s in context.Students
            orderby s.FirstMidName select s).ToList<Student>();

         Console.WriteLine("Retrieve all Students from the database:");

         foreach (var stdnt in students) {
            string name = stdnt.FirstMidName + " " + stdnt.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
         }

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được đầu ra sau và bạn sẽ thấy từ Đã chỉnh sửa được đính kèm trước tên đầu tiên như được hiển thị trong đầu ra sau.

Retrieve all Students from the database: 
ID: 1, Name: Edited Edited Alain Bomer 
ID: 2, Name: Edited Edited Mark Upston

Các tình huống bị ngắt kết nối

Tình huống ngắt kết nối là khi một thực thể được truy xuất từ ​​cơ sở dữ liệu và được sửa đổi trong ngữ cảnh khác. Giả sử chúng ta muốn hiển thị một số dữ liệu trong Lớp trình bày và chúng ta đang sử dụng một số ứng dụng n-tier, vì vậy sẽ tốt hơn nếu mở ngữ cảnh, tìm nạp dữ liệu và cuối cùng là đóng ngữ cảnh. Vì ở đây chúng tôi đã tìm nạp dữ liệu và đóng ngữ cảnh, các thực thể mà chúng tôi đã tìm nạp không còn được theo dõi nữa và đây là trường hợp bị ngắt kết nối.

Hãy xem đoạn mã sau, trong đó thực thể Sinh viên bị ngắt kết nối mới được thêm vào ngữ cảnh bằng phương pháp Thêm.

class Program {

   static void Main(string[] args) {

      var student = new Student {
         ID = 1001, 
         FirstMidName = "Wasim", 
         LastName = "Akram", 
         EnrollmentDate = DateTime.Parse( DateTime.Today.ToString())
      };

      using (var context = new MyContext()) {

         context.Students.Add(student);
         context.SaveChanges();

         //// Display all Students from the database

         var students = (from s in context.Students 
            orderby s.FirstMidName select s).ToList<Student>();

         Console.WriteLine("Retrieve all Students from the database:");

         foreach (var stdnt in students) {
            string name = stdnt.FirstMidName + " " + stdnt.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
         }

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau.

Retrieve all Students from the database:
ID: 1, Name: Edited Edited Edited Alain Bomer
ID: 2, Name: Edited Edited Edited Mark Upston
ID: 3, Name: Wasim Akram

LINQ cho các thực thể

Một trong những khái niệm quan trọng nhất để hiểu LINQ cho các Đối tượng là nó là một ngôn ngữ khai báo. Trọng tâm là xác định thông tin bạn cần, hơn là cách lấy thông tin.

  • Điều đó có nghĩa là bạn có thể dành nhiều thời gian hơn để làm việc với dữ liệu và ít thời gian hơn để tìm ra mã cơ bản cần thiết để thực hiện các tác vụ như truy cập cơ sở dữ liệu.

  • Điều quan trọng là phải hiểu rằng các ngôn ngữ khai báo không thực sự loại bỏ bất kỳ quyền kiểm soát nào khỏi nhà phát triển, nhưng nó giúp nhà phát triển tập trung sự chú ý vào những gì quan trọng.

Từ khóa cơ bản của LINQ cho các thực thể

Điều quan trọng là phải biết các từ khóa cơ bản được sử dụng để tạo truy vấn LINQ. Chỉ có một số từ khóa cần nhớ, nhưng bạn có thể kết hợp chúng theo nhiều cách khác nhau để thu được kết quả cụ thể. Danh sách sau đây chứa các từ khóa cơ bản này và cung cấp mô tả đơn giản về từng từ khóa.

Sơ không. Từ khoá & Mô tả
1

Ascending

Chỉ định rằng thao tác sắp xếp diễn ra từ phần tử nhỏ nhất (hoặc thấp nhất) của một dải ô đến phần tử cao nhất của một dải ô. Đây thường là cài đặt mặc định. Ví dụ: khi thực hiện sắp xếp theo bảng chữ cái, sắp xếp sẽ nằm trong phạm vi từ A đến Z.

2

By

Chỉ định trường hoặc biểu thức được sử dụng để triển khai một nhóm. Trường hoặc biểu thức xác định một khóa được sử dụng để thực hiện tác vụ nhóm.

3

Descending

Chỉ định rằng thao tác sắp xếp diễn ra từ phần tử lớn nhất (hoặc cao nhất) của một dải ô đến phần tử thấp nhất của một dải ô. Ví dụ: khi thực hiện sắp xếp theo bảng chữ cái, sắp xếp sẽ nằm trong phạm vi từ Z đến A.

4

Equals

Được sử dụng giữa các mệnh đề bên trái và bên phải của câu lệnh nối để nối nguồn dữ liệu ngữ cảnh chính với nguồn dữ liệu ngữ cảnh thứ cấp. Trường hoặc biểu thức ở bên trái của từ khóa bằng chỉ định nguồn dữ liệu chính, trong khi trường hoặc biểu thức ở bên phải của từ khóa bằng chỉ định nguồn dữ liệu phụ.

5

From

Chỉ định nguồn dữ liệu được sử dụng để lấy thông tin cần thiết và xác định một biến phạm vi. Biến này có cùng mục đích với một biến được sử dụng để lặp lại trong vòng lặp.

6

Group

Sắp xếp đầu ra thành các nhóm bằng cách sử dụng giá trị khóa mà bạn chỉ định. Sử dụng nhiều mệnh đề nhóm để tạo ra nhiều cấp độ tổ chức đầu ra. Thứ tự của các mệnh đề nhóm xác định độ sâu mà tại đó giá trị khóa cụ thể xuất hiện trong thứ tự nhóm. Bạn kết hợp từ khóa này với by để tạo một ngữ cảnh cụ thể.

7

In

Được sử dụng trong một số cách. Trong trường hợp này, từ khóa xác định nguồn cơ sở dữ liệu ngữ cảnh được sử dụng cho một truy vấn. Khi làm việc với một phép nối, từ khóa in được sử dụng cho từng nguồn cơ sở dữ liệu ngữ cảnh được sử dụng cho phép nối.

số 8

Into

Chỉ định một số nhận dạng mà bạn có thể sử dụng làm tham chiếu cho các mệnh đề truy vấn LINQ chẳng hạn như tham gia, nhóm và chọn.

9

Join

Tạo một nguồn dữ liệu duy nhất từ ​​hai nguồn dữ liệu có liên quan, chẳng hạn như trong thiết lập tổng thể / chi tiết. Một phép nối có thể chỉ định một phép nối bên trong, nhóm hoặc phép nối bên trái, với phép nối bên trong làm mặc định. Bạn có thể đọc thêm về tham gia tại msdn.microsoft.com

10

Let

Xác định một biến phạm vi mà bạn có thể sử dụng để lưu trữ kết quả biểu thức con trong biểu thức truy vấn. Thông thường, biến phạm vi được sử dụng để cung cấp đầu ra được liệt kê bổ sung hoặc để tăng hiệu quả của truy vấn (để một tác vụ cụ thể, chẳng hạn như tìm giá trị chữ thường của một chuỗi, không cần thực hiện nhiều lần).

11

On

Chỉ định trường hoặc biểu thức được sử dụng để triển khai một phép nối. Trường hoặc biểu thức xác định một phần tử chung cho cả hai nguồn dữ liệu theo ngữ cảnh.

12

Orderby

Tạo thứ tự sắp xếp cho truy vấn. Bạn có thể thêm từ khóa tăng dần hoặc giảm dần để kiểm soát thứ tự sắp xếp. Sử dụng nhiều mệnh đề theo thứ tự để tạo nhiều cấp độ sắp xếp. Thứ tự của các mệnh đề theo thứ tự xác định thứ tự mà các biểu thức sắp xếp được xử lý, vì vậy việc sử dụng một thứ tự khác sẽ dẫn đến kết quả khác nhau.

13

Where

Xác định những gì LINQ sẽ truy xuất từ ​​nguồn dữ liệu. Bạn sử dụng một hoặc nhiều biểu thức Boolean để xác định các chi tiết cụ thể của những gì cần truy xuất. Các biểu thức Boolean được phân tách với nhau bằng cách sử dụng && (AND) và || (HOẶC) toán tử.

14

Select

Xác định kết quả đầu ra từ truy vấn LINQ bằng cách chỉ định thông tin nào sẽ trả về. Câu lệnh này xác định kiểu dữ liệu của các phần tử mà LINQ trả về trong quá trình lặp.

Phép chiếu

Truy vấn chiếu cải thiện hiệu quả ứng dụng của bạn, bằng cách chỉ truy xuất các trường cụ thể từ cơ sở dữ liệu của bạn.

  • Sau khi có dữ liệu, bạn có thể chiếu hoặc lọc nó nếu cần để định hình dữ liệu trước khi xuất.

  • Nhiệm vụ chính của bất kỳ biểu thức LINQ to Entities nào là lấy dữ liệu và cung cấp dữ liệu dưới dạng đầu ra.

Phần “Phát triển LINQ cho các truy vấn đối tượng” của chương này trình bày các kỹ thuật để thực hiện nhiệm vụ cơ bản này.

Chúng ta hãy xem đoạn mã sau trong danh sách học sinh sẽ được truy xuất.

using (var context = new UniContextEntities()) {

   var studentList = from s in context.Students select s;

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }
}

Đối tượng duy nhất

Để truy xuất một đối tượng sinh viên duy nhất, bạn có thể sử dụng các phương thức liệt kê First () hoặc FirstOrDefault trả về phần tử đầu tiên của một chuỗi. Sự khác biệt giữa First và FirstOrDefault là First () sẽ ném một ngoại lệ, nếu không có dữ liệu kết quả cho các tiêu chí được cung cấp trong khi FirstOrDefault () trả về giá trị mặc định là null, nếu không có dữ liệu kết quả. Trong đoạn mã dưới đây, sinh viên đầu tiên từ danh sách sẽ được truy xuất có tên là Ali.

using (var context = new UniContextEntities()) {

   var student = (from s in context.Students where s.FirstMidName 
      == "Ali" select s).FirstOrDefault<Student>();

   string name = student.FirstMidName + " " + student.LastName;
   Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
}

Bạn cũng có thể sử dụng Single () hoặc SingleOrDefault để lấy một đối tượng sinh viên duy nhất trả về một phần tử cụ thể duy nhất của một chuỗi. Trong ví dụ sau, một sinh viên được truy xuất có ID là 2.

using (var context = new UniContextEntities()) {

   var student = (from s in context.Students where s.ID 
      == 2 select s).SingleOrDefault<Student>();
   string name = student.FirstMidName + " " + student.LastName;
	
   Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   Console.ReadKey();
}

Danh sách các đối tượng

Nếu bạn muốn lấy danh sách sinh viên có tên là Ali thì bạn có thể sử dụng phương thức liệt kê ToList ().

using (var context = new UniContextEntities()) {

   var studentList = (from s in context.Students where s.FirstMidName 
      == "Ali" select s).ToList();

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

Đặt hàng

Để truy xuất dữ liệu / danh sách theo bất kỳ thứ tự cụ thể nào, bạn có thể sử dụng từ khóa orderby. Trong đoạn mã sau, đoạn mã danh sách học sinh sẽ được truy xuất theo thứ tự tăng dần.

using (var context = new UniContextEntities()) {

   var studentList = (from s in context.Students orderby
      s.FirstMidName ascending select s).ToList();

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

Truy vấn khung thực thể chiếu Vs tiêu chuẩn

Giả sử, bạn có một mô hình Sinh viên có chứa ID, FirstMidName, LastName và EnrollmentDate. Nếu bạn muốn trả về danh sách Sinh viên, một truy vấn chuẩn sẽ trả về tất cả các trường. Nhưng nếu bạn chỉ muốn lấy danh sách sinh viên chứa các trường ID, FirstMidName và LastName. Đây là nơi bạn nên sử dụng truy vấn chiếu. Sau đây là ví dụ đơn giản về truy vấn phép chiếu.

using (var context = new UniContextEntities()) {

   var studentList = from s in context.Students
      orderby s.FirstMidName ascending
      where s.FirstMidName == "Ali"

   select new {s.ID, s.FirstMidName, s.LastName};

   foreach (var student in studentList) {
      string name = student.FirstMidName + " " + student.LastName;
      Console.WriteLine("ID : {0}, Name: {1}", student.ID, name);
   }

   Console.ReadKey();
}

Truy vấn chiếu ở trên loại trừ trường Ngày đăng ký. Điều này sẽ làm cho ứng dụng của bạn nhanh hơn nhiều.

Trong Entity Framework 6.0, một tính năng mới được giới thiệu được gọi là Logging SQL. Trong khi làm việc với Entity Framework, nó sẽ gửi các lệnh hoặc một truy vấn SQL tương đương đến cơ sở dữ liệu để thực hiện các hoạt động CRUD (Tạo, Đọc, Cập nhật và Xóa).

  • Tính năng này của Entity Framework là nắm bắt một truy vấn SQL tương đương được tạo bởi Entity Framework bên trong và cung cấp nó dưới dạng đầu ra.

  • Trước Entity Framework 6, bất cứ khi nào có nhu cầu theo dõi các truy vấn và lệnh cơ sở dữ liệu, nhà phát triển không có lựa chọn nào khác ngoài việc sử dụng một số tiện ích theo dõi bên thứ ba hoặc công cụ truy tìm cơ sở dữ liệu.

  • Trong Entity Framework 6, tính năng mới này cung cấp một cách đơn giản bằng cách ghi lại tất cả các hoạt động được thực hiện bởi Entity Framework.

  • Tất cả các hoạt động được thực hiện bởi Entity Framework đều được ghi lại bằng DbContext.Database.Log.

Hãy xem đoạn mã sau, trong đó một sinh viên mới được thêm vào cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Database.Log = Console.Write;

         // Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman", 
            LastName = "Khan", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         context.SaveChanges();
         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau, đây thực sự là nhật ký của tất cả các hoạt động được thực hiện bởi EF trong đoạn mã trên.

Opened connection at 10/28/2015 6:27:35 PM +05:00
Started transaction at 10/28/2015 6:27:35 PM +05:00
INSERT [dbo].[Student]([LastName], [FirstMidName], [EnrollmentDate])
VALUES (@0, @1, @2)
SELECT [ID]
FROM [dbo].[Student]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
-- @0: 'Khan' (Type = String, Size = -1)
-- @1: 'Salman' (Type = String, Size = -1)
-- @2: '10/28/2015 12:00:00 AM' (Type = DateTime)
-- Executing at 10/28/2015 6:27:35 PM +05:00
-- Completed in 5 ms with result: SqlDataReader
Committed transaction at 10/28/2015 6:27:35 PM +05:00
Closed connection at 10/28/2015 6:27:35 PM +05:00

Khi thuộc tính Nhật ký được đặt, các hoạt động sau được ghi lại:

  • SQL cho tất cả các loại lệnh khác nhau, ví dụ như Truy vấn, bao gồm chèn, cập nhật và xóa được tạo như một phần của SaveChanges

  • Parameters

  • Có hay không lệnh được thực thi không đồng bộ

  • Dấu thời gian cho biết thời điểm lệnh bắt đầu thực thi

  • Lệnh đã hoàn thành thành công hay không thành công

  • Một số dấu hiệu của giá trị kết quả

  • Khoảng thời gian gần đúng để thực hiện lệnh

Đăng nhập đến nơi khác

Nếu bạn đã có một số khuôn khổ ghi nhật ký và nó xác định một phương pháp ghi nhật ký thì bạn cũng có thể ghi nhật ký nó vào nơi khác.

Hãy xem ví dụ sau, trong đó chúng ta có một lớp MyLogger khác.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Database.Log = s ⇒ MyLogger.Log("EFLoggingDemo", s);

         // Create a new student and save it

         context.Students.Add(new Student {
            FirstMidName = "Salman", 
            LastName = "Khan", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         context.SaveChanges();
         Console.ReadKey();
      }
   }
}

public class MyLogger {

   public static void Log(string application, string message) {
      Console.WriteLine("Application: {0}, EF Message: {1} ",application, message);
   }
}

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong Entity Framework 6.0, có một tính năng mới khác được gọi là Interceptorhoặc Đánh chặn. Mã đánh chặn được xây dựng dựa trên khái niệminterception interfaces. Ví dụ, giao diện IDbCommandInterceptor xác định các phương thức được gọi trước khi EF thực hiện lệnh gọi ExecuteNonQuery, ExecuteScalar, ExecuteReader và các phương thức liên quan.

  • Entity Framework có thể thực sự tỏa sáng bằng cách sử dụng đánh chặn. Sử dụng cách tiếp cận này, bạn có thể nắm bắt được nhiều thông tin hơn một cách nhanh chóng mà không cần phải gỡ rối mã của mình.

  • Để thực hiện điều này, bạn cần tạo bộ đánh chặn tùy chỉnh của riêng mình và đăng ký nó cho phù hợp.

  • Khi một lớp triển khai giao diện IDbCommandInterceptor đã được tạo, nó có thể được đăng ký với Entity Framework bằng cách sử dụng lớp DbInterception.

  • Giao diện IDbCommandInterceptor có sáu phương thức và bạn cần triển khai tất cả các phương thức này. Sau đây là cách thực hiện cơ bản của các phương pháp này.

Hãy xem đoạn mã sau mà giao diện IDbCommandInterceptor được triển khai.

public class MyCommandInterceptor : IDbCommandInterceptor {

   public static void Log(string comm, string message) {
      Console.WriteLine("Intercepted: {0}, Command Text: {1} ", comm, message);
   }

   public void NonQueryExecuted(DbCommand command, 
      DbCommandInterceptionContext<int> interceptionContext) {
         Log("NonQueryExecuted: ", command.CommandText);
   }

   public void NonQueryExecuting(DbCommand command, 
      DbCommandInterceptionContext<int> interceptionContext) {
         Log("NonQueryExecuting: ", command.CommandText);
   }

   public void ReaderExecuted(DbCommand command, 
      DbCommandInterceptionContext<DbDataReader> interceptionContext) {
         Log("ReaderExecuted: ", command.CommandText);
   }

   public void ReaderExecuting(DbCommand command, 
      DbCommandInterceptionContext<DbDataReader> interceptionContext) {
         Log("ReaderExecuting: ", command.CommandText);
   }

   public void ScalarExecuted(DbCommand command, 
      DbCommandInterceptionContext<object> interceptionContext) {
         Log("ScalarExecuted: ", command.CommandText);
   }

   public void ScalarExecuting(DbCommand command, 
      DbCommandInterceptionContext<object> interceptionContext) {
         Log("ScalarExecuting: ", command.CommandText);
   }

}

Đăng ký Interceptors

Sau khi một lớp thực thi một hoặc nhiều giao diện đánh chặn đã được tạo, nó có thể được đăng ký với EF bằng cách sử dụng lớp DbInterception như được hiển thị trong đoạn mã sau.

DbInterception.Add(new MyCommandInterceptor());

Các bộ chặn cũng có thể được đăng ký ở cấp miền ứng dụng bằng cách sử dụng cấu hình dựa trên mã DbConfiguration như được hiển thị trong đoạn mã sau.

public class MyDBConfiguration : DbConfiguration {

   public MyDBConfiguration() {
      DbInterception.Add(new MyCommandInterceptor());
   }
}

Bạn cũng có thể định cấu hình tệp cấu hình bộ đánh chặn bằng cách sử dụng mã -

<entityFramework>
   <interceptors>
      <interceptor type = "EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/>
   </interceptors>
</entityFramework>

Hỗ trợ kiểu không gian đã được giới thiệu trong Entity Framework 5. Một tập hợp các toán tử cũng được bao gồm để cho phép các truy vấn phân tích dữ liệu không gian. Ví dụ: một truy vấn có thể lọc dựa trên khoảng cách giữa hai vị trí địa lý.

  • Entity Framework sẽ cho phép các kiểu dữ liệu không gian mới được hiển thị dưới dạng thuộc tính trên các lớp của bạn và ánh xạ chúng tới các cột không gian trong cơ sở dữ liệu của bạn.

  • Bạn cũng sẽ có thể viết các truy vấn LINQ sử dụng các toán tử không gian để lọc, sắp xếp và nhóm dựa trên các tính toán không gian được thực hiện trong cơ sở dữ liệu.

Có hai kiểu dữ liệu không gian chính:

  • Kiểu dữ liệu địa lý lưu trữ dữ liệu hình elip, ví dụ, tọa độ kinh độ và vĩ độ GPS.

  • Kiểu dữ liệu hình học đại diện cho hệ tọa độ Euclid (phẳng).

Chúng ta hãy xem xét ví dụ sau về sân Cricket.

Step 1 - Tạo dự án mới từ tùy chọn menu File → New → Project.

Step 2 - Trong ngăn bên trái, chọn Ứng dụng bảng điều khiển.

Step 3 - Nhấp chuột phải vào tên dự án và chọn Quản lý gói NuGet…

Step 4 - Cài đặt Entity Framework.

Step 5 - Thêm tham chiếu đến hợp ngữ System.Data.Entity và cũng thêm câu lệnh sử dụng System.Data.Spatial cho các kiểu dữ liệu không gian.

Step 6 - Thêm lớp sau vào tệp Program.cs.

public class CricketGround {
   public int ID { get; set; }
   public string Name { get; set; }
   public DbGeography Location { get; set; }
}

Step 7 - Ngoài việc xác định các thực thể, bạn cần xác định một lớp dẫn xuất từ ​​DbContext và hiển thị các thuộc tính DbSet <TEntity>.

Trong Program.cs thêm định nghĩa ngữ cảnh.

public partial class CricketGroundContext : DbContext {
   public DbSet<CricketGround> CricketGrounds { get; set; }
}

Step 8 - Thêm mã sau vào chức năng Chính, sẽ thêm hai đối tượng CricketGround mới vào ngữ cảnh.

class Program {

   static void Main(string[] args) {

      using (var context = new CricketGroundContext()) {

         context.CricketGrounds.Add(new CricketGround() {
            Name = "Shalimar Cricket Ground", 
            Location = DbGeography.FromText("POINT(-122.336106 47.605049)"), 
         });

         context.CricketGrounds.Add(new CricketGround() {
            Name = "Marghazar Stadium", Location = DbGeography
               .FromText("POINT(-122.335197 47.646711)"), 
         });

         context.SaveChanges();

         var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)");

         var cricketGround = (from cg in context.CricketGrounds
            orderby cg.Location.Distance(myLocation) select cg).FirstOrDefault();

         Console.WriteLine("The closest Cricket Ground to you is: {0}.", cricketGround.Name);
      }
   }
}

Thuộc tính không gian được khởi tạo bằng phương thức DbGeography.FromText. Điểm địa lý được biểu thị dưới dạng WellKnownText được chuyển tới phương thức và sau đó lưu dữ liệu. Sau đó đối tượng CricketGround sẽ được truy xuất nơi vị trí của nó gần vị trí được chỉ định nhất.

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

The closest Cricket Ground to you is: Marghazar Stadium

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Tính kế thừa giúp có thể tạo ra các mô hình phức tạp phản ánh tốt hơn cách các nhà phát triển suy nghĩ và cũng giảm bớt công việc cần thiết để tương tác với các mô hình đó. Kế thừa được sử dụng với các thực thể phục vụ cùng mục đích như kế thừa được sử dụng với các lớp, vì vậy các nhà phát triển đã biết những điều cơ bản về cách hoạt động của tính năng này.

Hãy xem ví dụ sau và bằng cách tạo một dự án ứng dụng bảng điều khiển mới.

Step 1 - Thêm Mô hình Dữ liệu Thực thể ADO.NET bằng cách nhấp chuột phải vào tên dự án và chọn Thêm → Mục mới…

Step 2 - Thêm một thực thể và đặt tên là Person bằng cách làm theo tất cả các bước được đề cập trong chương Cách tiếp cận đầu tiên của mô hình.

Step 3 - Thêm một số thuộc tính vô hướng như trong hình sau.

Step 4 - Chúng tôi sẽ thêm hai thực thể nữa StudentTeacher, sẽ kế thừa các thuộc tính từ Bảng Người.

Step 5 - Bây giờ thêm thực thể Sinh viên và chọn Người từ hộp kết hợp Loại cơ sở như thể hiện trong hình sau.

Step 6 - Tương tự thêm thực thể Giáo viên.

Step 7 - Bây giờ thêm thuộc tính vô hướng EnrollmentDate vào thực thể sinh viên và thuộc tính HireDate vào thực thể Giáo viên.

Step 8 - Hãy tiếp tục và tạo cơ sở dữ liệu.

Step 9 - Nhấp chuột phải vào bề mặt thiết kế và chọn Generate Database from Model…

Step 10- Để tạo Cơ sở dữ liệu mới, nhấp vào Kết nối Mới… Hộp thoại sau sẽ mở ra. Bấm OK.

Step 11- Nhấp vào Kết thúc. Điều này sẽ thêm tệp * .edmx.sql trong dự án. Bạn có thể thực thi các tập lệnh DDL trong Visual Studio bằng cách mở tệp .sql. Bây giờ nhấp chuột phải và chọn Execute.

Step 12 - Vào trình khám phá máy chủ, bạn sẽ thấy rằng cơ sở dữ liệu được tạo với ba bảng được chỉ định.

Step 13 - Bạn cũng có thể thấy rằng các lớp miền sau cũng được tạo tự động.

public partial class Person {
   public int ID { get; set; }
   public string FirstMidName { get; set; }
   public string LastName { get; set; }
}

public partial class Student : Person {
   public System.DateTime EnrollmentDate { get; set; }
}

public partial class Teacher : Person {
   public System.DateTime HireDate { get; set; }
}

Tiếp theo là lớp Context.

public partial class InheritanceModelContainer : DbContext {

   public InheritanceModelContainer() : 
      base("name = InheritanceModelContainer") {}

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      throw new UnintentionalCodeFirstException();
   }

   public virtual DbSet<Person> People { get; set; }
}

Hãy thêm một số Học sinh và Giáo viên vào cơ sở dữ liệu và sau đó truy xuất nó từ cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      using (var context = new InheritanceModelContainer()) {

         var student = new Student {
            FirstMidName = "Meredith", 
            LastName = "Alonso", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(student);

         var student1 = new Student {
            FirstMidName = "Arturo", 
            LastName = "Anand", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(student1);

         var techaer = new Teacher {
            FirstMidName = "Peggy", 
            LastName = "Justice", 
            HireDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(techaer);

         var techaer1 = new Teacher {
            FirstMidName = "Yan", 
            LastName = "Li", 
            HireDate = DateTime.Parse(DateTime.Today.ToString())
         };

         context.People.Add(techaer1);
         context.SaveChanges();
      }
   }
}

Học sinh và giáo viên được thêm vào cơ sở dữ liệu. NTo truy xuất học sinh và giáo viên,OfType phương thức này cần được sử dụng, phương thức này sẽ trả về Sinh viên và Giáo viên liên quan đến bộ phận được chỉ định.

Console.WriteLine("All students in database"); 
Console.WriteLine("");

foreach (var student in context.People.OfType<Student>()) {
   string name = student.FirstMidName + " " + student.LastName;
   Console.WriteLine("ID: {0}, Name: {1}, \tEnrollment Date {2} ", 
      student.ID, name, student.EnrollmentDate.ToString());
}

Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.WriteLine("");
Console.WriteLine("All teachers in database");
Console.WriteLine("");

foreach (var teacher in context.People.OfType<Teacher>()) {
   string name = teacher.FirstMidName + " " + teacher.LastName;
   Console.WriteLine("ID: {0}, Name: {1}, \tHireDate {2} ", 
      teacher.ID, name, teacher.HireDate.ToString()); 
}

Console.WriteLine("");
Console.WriteLine("************************************************************ *****");
Console.ReadKey();

Trong truy vấn đầu tiên, khi bạn sử dụng OfType <Student> () thì bạn sẽ không thể truy cập HireDate vì thuộc tính HireDate là một phần của Thực thể giáo viên và thuộc tính EnrollmentDate tương tự sẽ không thể truy cập được khi bạn sử dụng OfType <Teacher> ()

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau:

All students in database
ID: 1, Name: Meredith Alonso,   Enrollment Date 10/30/2015 12:00:00 AM
ID: 2, Name: Arturo Anand,      Enrollment Date 10/30/2015 12:00:00 AM
*****************************************************************  
All teachers in database
ID: 3, Name: Peggy Justice,     HireDate 10/30/2015 12:00:00 AM
ID: 4, Name: Yan Li,    HireDate 10/30/2015 12:00:00 AM
*****************************************************************

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong Entity Framework 5 và các phiên bản trước của Entity Framework, mã được phân chia giữa các thư viện lõi (chủ yếu là System.Data.Entity.dll) được vận chuyển như một phần của .NET Framework và các thư viện bổ sung (chủ yếu là EntityFramework.dll) đã được phân phối và được vận chuyển bằng NuGet như thể hiện trong sơ đồ sau.

Trong Entity Framework 6, các API cốt lõi trước đây là một phần của .NET framework cũng được vận chuyển và phân phối như một phần của gói NuGet.

Điều này là cần thiết để cho phép Entity Framework được tạo thành mã nguồn mở. Tuy nhiên, do đó, các ứng dụng sẽ cần được xây dựng lại bất cứ khi nào có nhu cầu di chuyển hoặc nâng cấp ứng dụng của bạn từ các phiên bản cũ hơn của Entity Framework lên EF 6.

Quá trình di chuyển diễn ra đơn giản nếu ứng dụng của bạn sử dụng DbContext, được chuyển trong EF 4.1 trở lên. Nhưng nếu ứng dụng của bạn là ObjectContext thì bạn cần thực hiện thêm một số công việc.

Hãy xem các bước sau bạn cần thực hiện để nâng cấp ứng dụng hiện có lên EF6.

Step 1 - Bước đầu tiên là nhắm mục tiêu .NET Framework 4.5.2 và sau đó nhấp chuột phải vào dự án của bạn và chọn thuộc tính.

Step 2 - Nhấp chuột phải vào dự án của bạn một lần nữa và chọn Quản lý gói NuGet ...

Step 3- Trong tab Trực tuyến, chọn EntityFramework và nhấp vào Cài đặt. Đảm bảo rằng các tham chiếu lắp ráp đến System.Data.Entity.dll đã bị loại bỏ.

Khi bạn cài đặt gói EF6 NuGet, nó sẽ tự động xóa mọi tham chiếu đến System.Data.Entity khỏi dự án của bạn cho bạn.

Step 4 - Nếu bạn có bất kỳ mô hình nào được tạo bằng EF Designer, thì bạn cũng sẽ cần cập nhật các mẫu tạo mã để tạo mã tương thích EF6.

Step 5 - Trong Trình khám phá Giải pháp của bạn dưới tệp edmx của bạn, hãy xóa các mẫu tạo mã hiện có thường được đặt tên là <edmx_file_name> .tt và <edmx_file_name> .Context.tt.

Step 6 - Mở mô hình của bạn trong EF Designer, nhấp chuột phải vào bề mặt thiết kế và chọn Thêm mục tạo mã ...

Step 7 - Thêm mẫu tạo mã EF 6.x thích hợp.

Nó cũng sẽ tự động tạo mã tương thích EF6.

Nếu các ứng dụng của bạn sử dụng EF 4.1 trở lên, bạn sẽ không cần thay đổi bất kỳ điều gì trong mã, vì không gian tên cho các loại DbContext và Code First không thay đổi.

Nhưng nếu ứng dụng của bạn đang sử dụng phiên bản Entity Framework cũ hơn thì các loại như ObjectContext trước đây trong System.Data.Entity.dll đã được chuyển sang không gian tên mới.

Step 8 - Bạn sẽ cần cập nhật chỉ thị sử dụng hoặc Nhập khẩu để xây dựng dựa trên EF6.

Quy tắc chung cho các thay đổi không gian tên là bất kỳ kiểu nào trong System.Data. * Đều được chuyển đến System.Data.Entity.Core. *. Sau đây là một số trong số họ -

  • System.Data.EntityException ⇒ System.Data.Entity.Core.EntityException
  • System.Data.Objects.ObjectContext ⇒ System.Data.Entity.Core.Objects.ObjectContext;
  • System.Data.Objects.DataClasses.RelationshipManager ⇒ System.Data.Entity.Core.Objects.DataClasses.RelationshipManager;

Một số kiểu nằm trong không gian tên Core vì chúng không được sử dụng trực tiếp cho hầu hết các ứng dụng dựa trên DbContext.

  • System.Data.EntityState ⇒ System.Data.Entity.EntityState
  • System.Data.Objects.DataClasses.EdmFunctionAttribute ⇒ System.Data.Entity.DbFunctionAttribute

Dự án Entity Framework hiện tại của bạn sẽ hoạt động trong Entity Framework 6.0 mà không có bất kỳ thay đổi lớn nào.

Tải háo hức là quá trình theo đó truy vấn cho một loại thực thể cũng tải các thực thể liên quan như một phần của truy vấn. Việc tải hứng thú đạt được bằng cách sử dụngInclude method.

Nó có nghĩa là yêu cầu dữ liệu liên quan được trả về cùng với kết quả truy vấn từ cơ sở dữ liệu. Chỉ có một kết nối được thực hiện với nguồn dữ liệu, một lượng dữ liệu lớn hơn được trả về trong truy vấn ban đầu.

Ví dụ: khi truy vấn sinh viên, hãy tải các đăng ký của họ. Các sinh viên và ghi danh của họ sẽ được truy xuất trong một truy vấn duy nhất.

Hãy xem ví dụ sau đây, trong đó tất cả sinh viên có đăng ký học tương ứng của họ được truy xuất từ ​​cơ sở dữ liệu bằng cách sử dụng tải háo hức.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {
         // Load all students and related enrollments
         var students = context.Students
            .Include(s ⇒ s.Enrollments).ToList();
			
         foreach (var student in students) {
            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
				
            foreach (var enrollment in student.Enrollments) {
               Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
                  enrollment.EnrollmentID, enrollment.CourseID);
            }
         }

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau.

ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
       Enrollment ID: 4, Course ID: 1045
       Enrollment ID: 5, Course ID: 3141
       Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
       Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
       Enrollment ID: 8, Course ID: 1050
       Enrollment ID: 9, Course ID: 4022

Dưới đây là một số dạng truy vấn tải mong muốn khác có thể được sử dụng.

// Load one Student and its related enrollments

var student1 = context.Students
   .Where(s ⇒ s.FirstMidName == "Ali")
   .Include(s ⇒ s.Enrollments).FirstOrDefault();

// Load all Students and related enrollments
// using a string to specify the relationship

var studentList = context.Students
   .Include("Enrollments").ToList();

// Load one Student and its related enrollments
// using a string to specify the relationship

var student = context.Students
   .Where(s ⇒ s.FirstMidName == "Salman")
   .Include("Enrollments").FirstOrDefault();

Nhiều cấp độ

Nó cũng có thể tải nhiều cấp độ của các thực thể liên quan. Các truy vấn sau đây hiển thị các ví dụ về Sinh viên, Ghi danh và Khóa học.

// Load all Students, all related enrollments, and all related courses

var studentList = context.Students
   .Include(s ⇒ s.Enrollments.Select(c ⇒ c.Course)).ToList();

// Load all Students, all related enrollments, and all related courses
// using a string to specify the relationships

var students = context.Students
   .Include("Enrollments.Course").ToList();

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Tải chậm là quá trình theo đó một thực thể hoặc tập hợp các thực thể được tải tự động từ cơ sở dữ liệu lần đầu tiên một thuộc tính tham chiếu đến thực thể / các thực thể được truy cập. Tải chậm có nghĩa là trì hoãn việc tải dữ liệu liên quan, cho đến khi bạn yêu cầu cụ thể.

  • Khi sử dụng các loại thực thể POCO, tải chậm được thực hiện bằng cách tạo các phiên bản của các loại proxy dẫn xuất và sau đó ghi đè các thuộc tính ảo để thêm móc tải.

  • Tải chậm là mặc định khá nhiều.

  • Nếu bạn để cấu hình mặc định và không nói rõ ràng với Entity Framework trong truy vấn của mình rằng bạn muốn thứ gì đó khác hơn là tải chậm, thì tải chậm là những gì bạn sẽ nhận được.

  • Ví dụ: khi sử dụng lớp thực thể Sinh viên, các Đăng ký liên quan sẽ được tải lần đầu tiên thuộc tính điều hướng Đăng ký được truy cập.

  • Thuộc tính điều hướng nên được xác định là công khai, ảo. Ngữ cảnh sẽNOT thực hiện tải chậm nếu thuộc tính không được xác định là ảo.

Sau đây là một lớp Sinh viên chứa thuộc tính điều hướng của Đăng ký.

public partial class Student {

   public Student() {
      this.Enrollments = new HashSet<Enrollment>();
   }
	
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public System.DateTime EnrollmentDate { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Chúng ta hãy xem xét một ví dụ đơn giản, trong đó danh sách sinh viên được tải từ cơ sở dữ liệu đầu tiên và sau đó nó sẽ tải đăng ký của một sinh viên cụ thể bất cứ khi nào bạn cần.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         //Loading students only
         IList<Student> students = context.Students.ToList<Student>();

         foreach (var student in students) {

            string name = student.FirstMidName + " " + student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
	
            foreach (var enrollment in student.Enrollments) {
               Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
                  enrollment.EnrollmentID, enrollment.CourseID);
            }
         }

         Console.ReadKey();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau.

ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
       Enrollment ID: 4, Course ID: 1045
       Enrollment ID: 5, Course ID: 3141
       Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
       Enrollment ID: 7, Course ID: 1050
ID: 4, Name: Gytis Barzdukas
       Enrollment ID: 8, Course ID: 1050
       Enrollment ID: 9, Course ID: 4022
ID: 5, Name: Yan Li
       Enrollment ID: 10, Course ID: 4041
ID: 6, Name: Peggy Justice
       Enrollment ID: 11, Course ID: 1045
ID: 7, Name: Laura Norman
       Enrollment ID: 12, Course ID: 3141

Tắt tính năng tải chậm

Tải chậm và tuần tự hóa không kết hợp với nhau và nếu không cẩn thận, bạn có thể kết thúc truy vấn cho toàn bộ cơ sở dữ liệu của mình chỉ vì tải chậm được bật. Bạn nên tắt tính năng tải chậm trước khi tuần tự hóa một thực thể.

Tắt cho Thuộc tính Điều hướng Cụ thể

Có thể tắt tải từng phần của bộ sưu tập Đăng ký bằng cách làm cho thuộc tính Đăng ký không ảo như được minh họa trong ví dụ sau.

public partial class Student { 

   public Student() { 
      this.Enrollments = new HashSet<Enrollment>(); 
   }
	
   public int ID { get; set; } 
   public string LastName { get; set; } 
   public string FirstMidName { get; set; } 
   public System.DateTime EnrollmentDate { get; set; }
   public ICollection<Enrollment> Enrollments { get; set; } 
}

Tắt cho tất cả các thực thể

Có thể tắt tính năng tải chậm cho tất cả các thực thể trong ngữ cảnh bằng cách đặt cờ trên thuộc tính Cấu hình thành false như được hiển thị trong ví dụ sau.

public partial class UniContextEntities : DbContext { 

   public UniContextEntities(): base("name=UniContextEntities") {
      this.Configuration.LazyLoadingEnabled = false;
   }
	
   protected override void OnModelCreating(DbModelBuilder modelBuilder) { 
      throw new UnintentionalCodeFirstException(); 
   } 
}

Sau khi tắt tính năng tải chậm, bây giờ khi bạn chạy lại ví dụ trên, bạn sẽ thấy rằng Danh sách đăng ký không được tải và chỉ có dữ liệu sinh viên được truy xuất.

ID: 1, Name: Ali Alexander
ID: 2, Name: Meredith Alons
ID: 3, Name: Arturo Anand
ID: 4, Name: Gytis Barzduka
ID: 5, Name: Yan Li
ID: 6, Name: Peggy Justice
ID: 7, Name: Laura Norman
ID: 8, Name: Nino Olivetto

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Khi bạn tắt tính năng tải chậm, vẫn có thể tải các thực thể liên quan một cách lười biếng, nhưng nó phải được thực hiện bằng một lệnh gọi rõ ràng.

  • Không giống như tải chậm, không có sự mơ hồ hoặc khả năng nhầm lẫn về thời điểm chạy truy vấn.

  • Để làm như vậy, bạn sử dụng phương thức Load trên mục nhập của thực thể liên quan.

  • Đối với mối quan hệ một-nhiều, hãy gọi phương thức Tải trên Bộ sưu tập.

  • Và đối với mối quan hệ một-một, hãy gọi phương thức Tải trên Tham chiếu.

Hãy xem ví dụ sau, trong đó tính năng tải chậm bị vô hiệu hóa và sau đó học sinh có tên là Ali được truy xuất.

Thông tin sinh viên sau đó được ghi trên bảng điều khiển. Nếu bạn nhìn vào mã, thông tin đăng ký cũng được ghi nhưng thực thể Đăng ký chưa được tải nên vòng lặp foreach sẽ không được thực thi.

Sau khi thực thể Đăng ký đó được tải rõ ràng, bây giờ thông tin sinh viên và thông tin tuyển sinh sẽ được ghi trên cửa sổ bảng điều khiển.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.LazyLoadingEnabled = false;

         var student = (from s in context.Students where s.FirstMidName == 
            "Ali" select s).FirstOrDefault<Student>();

         string name = student.FirstMidName + " " + student.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.WriteLine();
         Console.WriteLine("Explicitly loaded Enrollments");
         Console.WriteLine();

         context.Entry(student).Collection(s ⇒ s.Enrollments).Load();
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.ReadKey();
      }
   }
}

Khi ví dụ trên được thực thi, bạn sẽ nhận được kết quả sau. Chỉ thông tin sinh viên đầu tiên được hiển thị và sau khi tải thực thể tuyển sinh một cách rõ ràng, cả sinh viên và thông tin đăng ký liên quan của anh ta đều được hiển thị.

ID: 1, Name: Ali Alexander
Explicitly loaded Enrollments
ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong chương này, chúng ta hãy tìm hiểu về các kỹ thuật xác thực có thể được sử dụng trong ADO.NET Entity Framework để xác thực dữ liệu mô hình. Entity Framework cung cấp nhiều tính năng xác thực có thể được triển khai cho giao diện người dùng để xác thực phía máy khách hoặc có thể được sử dụng để xác thực phía máy chủ.

  • Trong Entity Framework, xác thực dữ liệu là một phần của giải pháp để bắt dữ liệu xấu trong ứng dụng.

  • Entity Framework xác thực tất cả dữ liệu trước khi nó được ghi vào cơ sở dữ liệu theo mặc định, sử dụng nhiều phương pháp xác thực dữ liệu.

  • Tuy nhiên, Entity Framework ra đời sau khi xác thực dữ liệu giao diện người dùng. Vì vậy, trong trường hợp đó, cần xác thực thực thể để xử lý bất kỳ trường hợp ngoại lệ nào mà EF ném ra và hiển thị một thông báo chung.

  • Có một số kỹ thuật xác thực dữ liệu để cải thiện việc kiểm tra lỗi của bạn và cách chuyển lại thông báo lỗi cho người dùng.

DbContext có một phương thức Overridable được gọi là ValidateEntity. Khi bạn gọi SaveChanges, Entity Framework sẽ gọi phương thức này cho từng thực thể trong bộ đệm ẩn của nó có trạng thái không phải là Unchanged. Bạn có thể đặt logic xác thực trực tiếp tại đây như được hiển thị trong ví dụ sau cho Đối tượng Sinh viên.

public partial class UniContextEntities : DbContext {

   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>("FirstMidName") == "") {

               var list = new List<System.Data.Entity
                  .Validation.DbValidationError>();

               list.Add(new System.Data.Entity.Validation
                  .DbValidationError("FirstMidName", "FirstMidName is required"));

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

         if (entityEntry.CurrentValues.GetValue<string>("LastName") == "") {

            var list = new List<System.Data.Entity
               .Validation.DbValidationError>();

            list.Add(new System.Data.Entity.Validation
               .DbValidationError("LastName", "LastName is required"));

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

         return base.ValidateEntity(entityEntry, items);
   }
}

Trong phương thức ValidateEntity ở trên, các thuộc tính FirstMidName và LastName của thực thể Student được kiểm tra nếu bất kỳ thuộc tính nào trong số này có chuỗi trống, sau đó nó sẽ trả về thông báo lỗi.

Hãy xem một ví dụ đơn giản trong đó một sinh viên mới được tạo, nhưng FirstMidName của sinh viên đó là chuỗi trống như được hiển thị trong đoạn mã sau.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         Console.WriteLine("Adding new Student to the database");
         Console.WriteLine();

         try {

            context.Students.Add(new Student() {
               FirstMidName = "",
               LastName = "Upston"
            });

            context.SaveChanges();
         } catch (DbEntityValidationException dbValidationEx) {

            foreach (DbEntityValidationResult entityErr in 
               dbValidationEx.EntityValidationErrors) {

               foreach (DbValidationError error in entityErr.ValidationErrors) {
                  Console.WriteLine("Error: {0}",error.ErrorMessage);
               }
            }
         }

         Console.ReadKey();
      }
   }
}

Khi ví dụ trên được biên dịch và thực thi, bạn sẽ nhận được thông báo lỗi sau trên cửa sổ giao diện điều khiển.

Adding new Student to the database  
Error: FirstMidName is required

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Entity Framework cung cấp khả năng theo dõi các thay đổi được thực hiện đối với các thực thể và mối quan hệ của chúng, do đó, các cập nhật chính xác được thực hiện trên cơ sở dữ liệu khi ngữ cảnh phương thức SaveChanges được gọi. Đây là một tính năng chính của Khung thực thể.

  • Theo dõi thay đổi theo dõi các thay đổi trong khi thêm (các) bản ghi mới vào tập hợp thực thể, sửa đổi hoặc xóa các thực thể hiện có.

  • Sau đó, tất cả các thay đổi được giữ bởi mức DbContext.

  • Những thay đổi theo dõi này sẽ bị mất nếu chúng không được lưu trước khi đối tượng DbContext bị hủy.

  • Lớp DbChangeTracker cung cấp cho bạn tất cả thông tin về các thực thể hiện tại đang được theo dõi bởi ngữ cảnh.

  • Để theo dõi bất kỳ thực thể nào theo ngữ cảnh, nó phải có thuộc tính khóa chính.

Trong Khung thực thể, theo dõi thay đổi được bật theo mặc định. Bạn cũng có thể tắt theo dõi thay đổi bằng cách đặt thuộc tính AutoDetectChangesEnabled của DbContext thành false. Nếu thuộc tính này được đặt thành true thì Khung thực thể duy trì trạng thái của các thực thể.

using (var context = new UniContextEntities()) {
   context.Configuration.AutoDetectChangesEnabled = true;
}

Hãy xem ví dụ sau, trong đó các sinh viên và đăng ký của họ được truy xuất từ ​​cơ sở dữ liệu.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.AutoDetectChangesEnabled = true;
         Console.WriteLine("Retrieve Student");

         var student = (from s in context.Students where s.FirstMidName == 
            "Ali" select s).FirstOrDefault<Student>();

         string name = student.FirstMidName + " " + student.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
         Console.WriteLine();
         Console.WriteLine("Retrieve all related enrollments");

         foreach (var enrollment in student.Enrollments) {
            Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
               enrollment.EnrollmentID, enrollment.CourseID);
         }

         Console.WriteLine();

         Console.WriteLine("Context tracking changes of {0} entity.", 
            context.ChangeTracker.Entries().Count());

         var entries = context.ChangeTracker.Entries();

         foreach (var entry in entries) {
            Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
            Console.WriteLine("Status: {0}", entry.State);
         }

         Console.ReadKey();
      }
   }
}

Khi ví dụ trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau.

Retrieve Student 
ID: 1, Name: Ali Alexander
Retrieve all related enrollments
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
Context tracking changes of 4 entity.
Entity Name: Student
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged
Entity Name: Enrollment
Status: Unchanged

Bạn có thể thấy rằng tất cả dữ liệu chỉ được truy xuất từ ​​cơ sở dữ liệu, đó là lý do tại sao trạng thái không thay đổi đối với tất cả các thực thể.

Bây giờ chúng ta hãy xem một ví dụ đơn giản khác, trong đó chúng ta sẽ thêm một đăng ký nữa và xóa một học sinh khỏi cơ sở dữ liệu. Sau đây là mã ghi danh mới được thêm vào và xóa một học sinh.

class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         context.Configuration.AutoDetectChangesEnabled = true;

         Enrollment enr = new Enrollment() { 
            StudentID = 1, CourseID = 3141 
         };

         Console.WriteLine("Adding New Enrollment");
         context.Enrollments.Add(enr);
         Console.WriteLine("Delete Student");

         var student = (from s in context.Students where s.ID == 
            23 select s).SingleOrDefault<Student>();

         context.Students.Remove(student);
         Console.WriteLine("");

         Console.WriteLine("Context tracking changes of {0} entity.", 
            context.ChangeTracker.Entries().Count());
         var entries = context.ChangeTracker.Entries();

         foreach (var entry in entries) {
            Console.WriteLine("Entity Name: {0}", entry.Entity.GetType().Name);
            Console.WriteLine("Status: {0}", entry.State);
         }

         Console.ReadKey();
      }
   }
}

Khi ví dụ trên được biên dịch và thực thi, bạn sẽ nhận được kết quả sau.

Adding New Enrollment
Delete Student
Context tracking changes of 2 entity.
Entity Name: Enrollment
Status: Added
Entity Name: Student
Status: Deleted

Bây giờ bạn có thể thấy rằng trạng thái của đối tượng đăng ký được đặt thành đã thêm và trạng thái của đối tượng sinh viên bị xóa, vì đăng ký mới đã được thêm vào và một học sinh bị xóa khỏi cơ sở dữ liệu.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong Entity Framework, Colored Entity chủ yếu xoay quanh việc thay đổi màu của thực thể trong trình thiết kế để các nhà phát triển dễ dàng xác định các nhóm thực thể liên quan trong trình thiết kế Visual Studio. Tính năng này lần đầu tiên được giới thiệu trong Entity Framework 5.0.

  • Tính năng này không liên quan gì đến khía cạnh hiệu suất.

  • Khi bạn có một dự án quy mô lớn và nhiều thực thể trong một tệp edmx, thì tính năng này rất hữu ích để tách các thực thể của bạn trong các mô-đun khác nhau.

Nếu bạn đang làm việc với tệp edmx và bạn đã mở nó trong trình thiết kế, để thay đổi màu sắc, hãy chọn một thực thể trên cửa sổ thiết kế. Sau đó nhấp chuột phải và chọn Thuộc tính.

Trong cửa sổ Thuộc tính, chọn thuộc tính Tô màu.

Chỉ định màu bằng tên màu hợp lệ, ví dụ: Xanh lục hoặc RGB hợp lệ (255, 128, 128) hoặc bạn cũng có thể chọn từ bộ chọn màu.

Để thay đổi màu của nhiều thực thể trong một lần, hãy chọn nhiều thực thể và thay đổi Màu tô cho tất cả chúng bằng cách sử dụng cửa sổ thuộc tính.

Bạn cũng có thể thay đổi định dạng của thuộc tính bằng cách chọn bất kỳ tùy chọn nào sau đây:

  • Tên hiển thị
  • Tên và loại hiển thị

Theo mặc định, tùy chọn tên hiển thị được chọn. Để thay đổi định dạng thuộc tính, bấm chuột phải vào cửa sổ trình thiết kế.

Chọn Định dạng thuộc tính vô hướng → Tên và loại hiển thị.

Bây giờ bạn có thể thấy rằng loại cũng được hiển thị cùng với tên.

Khung thực thể cung cấp ba cách tiếp cận để tạo một mô hình thực thể và mỗi cách tiếp cận đều có ưu và nhược điểm riêng.

  • Mã đầu tiên
  • Cơ sở dữ liệu đầu tiên
  • Mô hình đầu tiên

Trong chương này, chúng tôi sẽ mô tả ngắn gọn cách tiếp cận mã đầu tiên. Một số nhà phát triển thích làm việc với Designer trong Code trong khi những người khác chỉ thích làm việc với mã của họ. Đối với những nhà phát triển đó, Entity Framework có quy trình làm việc mô hình hóa được gọi là Code First.

  • Dòng công việc lập mô hình Code First nhắm mục tiêu đến một cơ sở dữ liệu không tồn tại và Code First sẽ tạo nó.

  • Nó cũng có thể được sử dụng nếu bạn có một cơ sở dữ liệu trống và sau đó Code First sẽ thêm các bảng mới vào đó.

  • Code First cho phép bạn xác định mô hình của mình bằng cách sử dụng các lớp C # hoặc VB.Net.

  • Cấu hình bổ sung có thể được thực hiện tùy chọn bằng cách sử dụng các thuộc tính trên các lớp và thuộc tính của bạn hoặc bằng cách sử dụng một API thông thạo.

Tại sao phải viết mã đầu tiên?

  • Code First thực sự được tạo thành từ một tập hợp các mảnh ghép. Đầu tiên là các lớp miền của bạn.

  • Các lớp miền không liên quan gì đến Entity Framework. Chúng chỉ là các mặt hàng thuộc miền doanh nghiệp của bạn.

  • Sau đó, Entity Framework có bối cảnh quản lý sự tương tác giữa các lớp đó và cơ sở dữ liệu của bạn.

  • Bối cảnh không dành riêng cho Code First. Đó là một tính năng Khung thực thể.

  • Code First thêm một trình tạo mô hình kiểm tra các lớp của bạn mà ngữ cảnh đang quản lý, sau đó sử dụng một tập hợp các quy tắc hoặc quy ước để xác định cách các lớp đó và các mối quan hệ mô tả một mô hình và cách mô hình đó sẽ ánh xạ đến cơ sở dữ liệu của bạn.

  • Tất cả điều này xảy ra trong thời gian chạy. Bạn sẽ không bao giờ nhìn thấy mô hình này, nó chỉ là trong bộ nhớ.

  • Code First cũng có khả năng sử dụng mô hình đó để tạo cơ sở dữ liệu nếu bạn muốn.

  • Nó cũng có thể cập nhật cơ sở dữ liệu nếu mô hình thay đổi, sử dụng một tính năng được gọi là Code First Migrations.

Thiết lập môi trường

Để bắt đầu làm việc với EF Code Cách tiếp cận đầu tiên, bạn cần cài đặt các công cụ sau trên hệ thống của mình.

  • Visual Studio 2013 (.net framework 4.5.2) hoặc phiên bản mới hơn.
  • MS SQL Server 2012 trở lên.
  • Khung thực thể thông qua Gói NuGet.

Cài đặt EF qua Gói NuGet

Step 1 - Đầu tiên, tạo ứng dụng console từ Tệp → Mới → Dự án…

Step 2 - Chọn Windows từ ngăn bên trái và Ứng dụng Bảng điều khiển từ ngăn mẫu.

Step 3 - Nhập EFCodeFirstDemo làm tên và chọn OK.

Step 4 - Nhấp chuột phải vào dự án của bạn trong trình khám phá giải pháp và chọn Quản lý Gói NuGet…

Thao tác này sẽ mở Trình quản lý gói NuGet và tìm kiếm EntityFramework. Thao tác này sẽ tìm kiếm tất cả các gói liên quan đến Entity Framework.

Step 5- Chọn EntityFramework và nhấp vào Cài đặt. Hoặc từ menu Công cụ, nhấp vào Trình quản lý gói NuGet và sau đó nhấp vào Bảng điều khiển Trình quản lý gói. Trong cửa sổ Bảng điều khiển Trình quản lý Gói, nhập lệnh sau: Install-Package EntityFramework.

Khi quá trình cài đặt hoàn tất, bạn sẽ thấy thông báo sau trong cửa sổ xuất “Đã cài đặt thành công 'EntityFramework 6.1.2' vào EFCodeFirstDemo”.

Sau khi cài đặt, EntityFramework.dll sẽ được đưa vào dự án của bạn, như thể hiện trong hình sau.

Bây giờ bạn đã sẵn sàng để bắt đầu làm việc với cách tiếp cận Code First.

Hãy xác định một mô hình rất đơn giản bằng cách sử dụng các lớp. Chúng tôi chỉ định nghĩa chúng trong tệp Program.cs nhưng trong một ứng dụng thực tế, bạn sẽ chia các lớp của mình thành các tệp riêng biệt và có khả năng là một dự án riêng biệt. Sau đây là mô hình dữ liệu mà chúng tôi sẽ tạo bằng cách sử dụng phương pháp Code First.

Tạo mô hình

Thêm ba lớp sau vào tệp Program.cs bằng cách sử dụng mã sau cho lớp Sinh viên.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • Thuộc tính ID sẽ trở thành cột khóa chính của bảng cơ sở dữ liệu tương ứng với lớp này.

  • Thuộc tính Enrollments là một thuộc tính điều hướng. Thuộc tính điều hướng giữ các thực thể khác có liên quan đến thực thể này.

  • Trong trường hợp này, thuộc tính Ghi danh của một thực thể Sinh viên sẽ giữ tất cả các thực thể Ghi danh có liên quan đến thực thể Sinh viên đó.

  • Các thuộc tính điều hướng thường được định nghĩa là ảo để chúng có thể tận dụng một số chức năng của Entity Framework chẳng hạn như tải chậm.

  • Nếu một thuộc tính điều hướng có thể chứa nhiều thực thể (như trong mối quan hệ nhiều-nhiều hoặc một-nhiều), thì loại của nó phải là danh sách trong đó các mục có thể được thêm, xóa và cập nhật, chẳng hạn như ICollection.

Sau đây là cách thực hiện cho lớp Khóa học.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Thuộc tính Enrollments là một thuộc tính điều hướng. Thực thể Khóa học có thể liên quan đến bất kỳ số lượng thực thể Đăng ký nào.

Sau đây là cách thực hiện cho Enrollment class và enum.

public enum Grade {
   A, B, C, D, F
}

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}
  • Thuộc tính EnrollmentID sẽ là khóa chính.

  • Thuộc tính Grade là một enum. Dấu chấm hỏi sau khai báo loại Grade cho biết thuộc tính Grade là vô hiệu.

  • Điểm rỗng khác với điểm 0. Không có nghĩa là một điểm chưa được biết hoặc chưa được chỉ định.

  • Thuộc tính StudentID và CourseID là khóa ngoại, và các thuộc tính điều hướng tương ứng là Student và Course.

  • Thực thể Ghi danh được liên kết với một Sinh viên và một thực thể Khóa học, vì vậy thuộc tính chỉ có thể chứa một thực thể Sinh viên và Khóa học duy nhất.

Tạo bối cảnh cơ sở dữ liệu

Lớp chính điều phối chức năng Entity Framework cho một mô hình dữ liệu nhất định là lớp ngữ cảnh cơ sở dữ liệu cho phép truy vấn và lưu dữ liệu. Bạn có thể tạo lớp này bằng cách lấy từ lớp DbContext và hiển thị một DbSet đã nhập cho mỗi lớp trong mô hình của chúng tôi. Sau đây là cách triển khai trên lớp MyContext, có nguồn gốc từ lớp DbContext.

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Sau đây là mã hoàn chỉnh trong tệp Program.cs.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   public enum Grade {
      A, B, C, D, F
   }

   public class Enrollment {
      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Grade? Grade { get; set; }
		
      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Đoạn mã trên là tất cả những gì chúng ta cần để bắt đầu lưu trữ và truy xuất dữ liệu. Hãy thêm một số dữ liệu và sau đó truy xuất nó. Sau đây là mã trong phương thức chính.

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
		
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
		
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ nhận được kết quả sau.

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

Bây giờ câu hỏi xuất hiện trong đầu là, đâu là dữ liệu và cơ sở dữ liệu mà chúng ta đã thêm một số dữ liệu và sau đó truy xuất nó từ cơ sở dữ liệu. Theo quy ước, DbContext đã tạo một cơ sở dữ liệu cho bạn.

  • Nếu một phiên bản SQL Express cục bộ có sẵn thì Code First đã tạo cơ sở dữ liệu trên phiên bản đó.

  • Nếu SQL Express không khả dụng, thì Code First sẽ thử và sử dụng LocalDb.

  • Cơ sở dữ liệu được đặt tên theo tên đủ điều kiện của ngữ cảnh dẫn xuất.

Trong trường hợp của chúng tôi, phiên bản SQL Express có sẵn và tên cơ sở dữ liệu là EFCodeFirstDemo.MyContext như thể hiện trong hình sau.

  • Đây chỉ là những quy ước mặc định và có nhiều cách khác nhau để thay đổi cơ sở dữ liệu mà Code First sử dụng.

  • Như bạn có thể thấy trong hình trên, nó đã tạo các bảng Sinh viên, Khóa học và Đăng ký và mỗi bảng chứa các cột có kiểu dữ liệu và độ dài thích hợp.

  • Tên cột và kiểu dữ liệu cũng khớp với thuộc tính của các lớp miền tương ứng.

Khởi tạo cơ sở dữ liệu

Trong ví dụ trên, chúng ta đã thấy rằng Code First tạo cơ sở dữ liệu tự động, nhưng nếu bạn muốn thay đổi tên của cơ sở dữ liệu và máy chủ, hãy xem cách Code First quyết định tên cơ sở dữ liệu và máy chủ trong khi khởi tạo cơ sở dữ liệu. Hãy xem sơ đồ sau.

Bạn có thể xác định hàm tạo cơ sở của lớp ngữ cảnh theo những cách sau.

  • Không có thông số
  • Tên cơ sở dữ liệu
  • Tên chuỗi kết nối

Không có thông số

Nếu bạn chỉ định phương thức khởi tạo cơ sở của lớp ngữ cảnh mà không có bất kỳ tham số nào như được hiển thị trong ví dụ trên, thì khung thực thể sẽ tạo cơ sở dữ liệu trong máy chủ SQLEXPRESS cục bộ của bạn với tên {Namespace}. {Context class name}.

Trong ví dụ trên, cơ sở dữ liệu được tạo tự động có tên EFCodeFirstDemo.MyContext. Nếu bạn nhìn vào tên, bạn sẽ thấy rằng EFCodeFirstDemo là không gian tên và MyContext là tên lớp ngữ cảnh như được hiển thị trong đoạn mã sau.

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Tên cơ sở dữ liệu

Nếu bạn chuyển tên cơ sở dữ liệu làm tham số trong phương thức khởi tạo cơ sở của lớp ngữ cảnh, thì Code First sẽ tự động tạo lại cơ sở dữ liệu, nhưng lần này tên sẽ là tên được truyền dưới dạng tham số trong phương thức khởi tạo cơ sở trên máy chủ cơ sở dữ liệu SQLEXPRESS cục bộ .

Trong đoạn mã sau, MyContextDB được chỉ định làm tham số trong hàm tạo cơ sở. Nếu chạy ứng dụng của bạn, thì cơ sở dữ liệu có tên MyContextDB sẽ được tạo trong máy chủ SQL cục bộ của bạn.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Tên chuỗi kết nối

Đây là một cách dễ dàng để yêu cầu DbContext sử dụng máy chủ cơ sở dữ liệu khác với SQL Express hoặc LocalDb. Bạn có thể chọn đặt một chuỗi kết nối trong tệp app.config của mình.

  • Nếu tên của chuỗi kết nối khớp với tên của ngữ cảnh của bạn (có hoặc không có đủ điều kiện không gian tên), thì nó sẽ được DbContext tìm thấy khi hàm tạo ít tham số được sử dụng.

  • Nếu tên chuỗi kết nối khác với tên ngữ cảnh của bạn, thì bạn có thể yêu cầu DbContext sử dụng kết nối này trong chế độ Code First bằng cách chuyển tên chuỗi kết nối tới hàm tạo DbContext.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • Trong đoạn mã trên, đoạn mã của chuỗi kết nối lớp ngữ cảnh được chỉ định làm tham số trong hàm tạo cơ sở.

  • Tên chuỗi kết nối phải bắt đầu bằng "name =" nếu không, nó sẽ coi nó như một tên cơ sở dữ liệu.

  • Biểu mẫu này nói rõ rằng bạn muốn tìm thấy chuỗi kết nối trong tệp cấu hình của mình. Một ngoại lệ sẽ được đưa ra nếu không tìm thấy một chuỗi kết nối với tên đã cho.

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Tên cơ sở dữ liệu trong chuỗi kết nối trong app.config là EFMyContextDB. CodeFirst sẽ tạo mộtEFMyContextDB cơ sở dữ liệu hoặc sử dụng hiện có EFMyContextDB cơ sở dữ liệu tại SQL Server cục bộ.

Các lớp miền

Cho đến nay, chúng tôi chỉ cho phép EF khám phá mô hình bằng cách sử dụng các quy ước mặc định của nó, nhưng sẽ có lúc các lớp của chúng tôi không tuân theo các quy ước và chúng tôi cần có thể thực hiện cấu hình thêm. Nhưng bạn có thể ghi đè các quy ước này bằng cách định cấu hình các lớp miền của mình để cung cấp cho EF thông tin cần thiết. Có hai tùy chọn để định cấu hình các lớp miền của bạn -

  • Chú thích dữ liệu
  • API thông thạo

Chú thích dữ liệu

DataAnnotations được sử dụng để định cấu hình các lớp của bạn, sẽ làm nổi bật các cấu hình cần thiết nhất. DataAnnotations cũng được hiểu bởi một số ứng dụng .NET, chẳng hạn như ASP.NET MVC, cho phép các ứng dụng này tận dụng các chú thích giống nhau để xác thực phía máy khách.

Sau đây là các chú thích dữ liệu được sử dụng trong lớp sinh viên.

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

API thông thạo

Hầu hết cấu hình mô hình có thể được thực hiện bằng cách sử dụng chú thích dữ liệu đơn giản. API thông thạo là một cách nâng cao để chỉ định cấu hình mô hình bao gồm mọi thứ mà chú thích dữ liệu có thể làm, ngoài một số cấu hình nâng cao hơn không thể thực hiện với chú thích dữ liệu. Chú thích dữ liệu và API thông thạo có thể được sử dụng cùng nhau.

Để truy cập API thông thạo, bạn ghi đè phương thức OnModelCreating trong DbContext. Bây giờ, hãy đổi tên cột trong bảng sinh viên từ FirstMidName thành FirstName như trong đoạn mã sau.

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

DataAnnotations được sử dụng để định cấu hình các lớp sẽ làm nổi bật các cấu hình cần thiết nhất. DataAnnotations cũng được hiểu bởi một số ứng dụng .NET, chẳng hạn như ASP.NET MVC cho phép các ứng dụng này tận dụng các chú thích giống nhau để xác nhận phía máy khách. Thuộc tính DataAnnotation ghi đè các quy ước CodeFirst mặc định.

System.ComponentModel.DataAnnotations bao gồm các thuộc tính sau đây ảnh hưởng đến khả năng vô hiệu hoặc kích thước của cột.

  • Key
  • Timestamp
  • ConcurrencyCheck
  • Required
  • MinLength
  • MaxLength
  • StringLength

System.ComponentModel.DataAnnotations.Schema không gian tên bao gồm các thuộc tính sau đây tác động đến lược đồ của cơ sở dữ liệu.

  • Table
  • Column
  • Index
  • ForeignKey
  • NotMapped
  • InverseProperty

Chìa khóa

Khung thực thể dựa vào mọi thực thể có một giá trị khóa mà nó sử dụng để theo dõi các thực thể. Một trong những quy ước mà Code First phụ thuộc vào là cách nó ngụ ý thuộc tính nào là khóa trong mỗi lớp Code First.

  • Quy ước là tìm kiếm một thuộc tính có tên “Id” hoặc một thuộc tính kết hợp tên lớp và “Id”, chẳng hạn như “StudentId”.

  • Thuộc tính sẽ ánh xạ đến một cột khóa chính trong cơ sở dữ liệu.

  • Các lớp Sinh viên, Khóa học và Ghi danh tuân theo quy ước này.

Bây giờ, hãy giả sử lớp Sinh viên sử dụng tên StdntID thay vì ID. Khi Code First không tìm thấy thuộc tính phù hợp với quy ước này, nó sẽ đưa ra một ngoại lệ vì yêu cầu của Entity Framework là bạn phải có thuộc tính khóa. Bạn có thể sử dụng chú thích khóa để chỉ định thuộc tính nào sẽ được sử dụng làm EntityKey.

Hãy xem đoạn mã sau của một lớp Sinh viên có chứa StdntID, nhưng nó không tuân theo quy ước mặc định của Code First. Vì vậy, để xử lý điều này, một thuộc tính Khóa được thêm vào sẽ làm cho nó trở thành khóa chính.

public class Student {

   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Khi bạn chạy ứng dụng của mình và xem xét cơ sở dữ liệu của mình trong SQL Server Explorer, bạn sẽ thấy rằng khóa chính bây giờ là StdntID trong bảng Học sinh.

Entity Framework cũng hỗ trợ các khóa tổng hợp. Composite keyscũng là các khóa chính bao gồm nhiều thuộc tính. Ví dụ: bạn có một lớp DrivingLicense có khóa chính là sự kết hợp của LicenseNumber và Iss IssueCountry.

public class DrivingLicense {

   [Key, Column(Order = 1)]
   public int LicenseNumber { get; set; }
   [Key, Column(Order = 2)]
   public string IssuingCountry { get; set; }
   public DateTime Issued { get; set; }
   public DateTime Expires { get; set; }
}

Khi bạn có các khóa tổng hợp, Entity Framework yêu cầu bạn xác định thứ tự của các thuộc tính khóa. Bạn có thể thực hiện việc này bằng cách sử dụng chú thích Cột để chỉ định đơn hàng.

Dấu thời gian

Code First sẽ coi thuộc tính Dấu thời gian giống như thuộc tính ConcurrencyCheck, nhưng nó cũng sẽ đảm bảo rằng trường cơ sở dữ liệu mà mã tạo ra đầu tiên là không thể null.

  • Phổ biến hơn là sử dụng trường chuyển đổi hàng hoặc trường dấu thời gian để kiểm tra đồng thời.

  • Thay vì sử dụng chú thích ConcurrencyCheck, bạn có thể sử dụng chú thích TimeStamp cụ thể hơn miễn là loại thuộc tính là mảng byte.

  • Bạn chỉ có thể có một thuộc tính dấu thời gian trong một lớp nhất định.

Hãy xem một ví dụ đơn giản bằng cách thêm thuộc tính TimeStamp vào lớp Khóa học -

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp]
   public byte[] TStamp { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Như bạn có thể thấy trong ví dụ trên, thuộc tính Dấu thời gian được áp dụng cho thuộc tính Byte [] của lớp Khóa học. Vì vậy, Code First sẽ tạo một cột dấu thời gian TStamptrong bảng Khóa học.

Kiểm tra đồng thời

Chú thích ConcurrencyCheck cho phép bạn gắn cờ một hoặc nhiều thuộc tính được sử dụng để kiểm tra đồng thời trong cơ sở dữ liệu khi người dùng chỉnh sửa hoặc xóa một thực thể. Nếu bạn đã làm việc với EF Designer, điều này phù hợp với việc đặt ConcurrencyMode của thuộc tính thành Cố định.

Hãy xem một ví dụ đơn giản về cách ConcurrencyCheck hoạt động bằng cách thêm nó vào thuộc tính Title trong lớp Course.

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp, DataType("timestamp")]
   public byte[] TimeStamp { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Trong lớp Khóa học ở trên, thuộc tính ConcurrencyCheck được áp dụng cho thuộc tính Title hiện có. Bây giờ, Code First sẽ bao gồm cột Title trong lệnh cập nhật để kiểm tra tính đồng thời lạc quan như được hiển thị trong đoạn mã sau.

exec sp_executesql N'UPDATE [dbo].[Courses]
   SET [Title] = @0
   WHERE (([CourseID] = @1) AND ([Title] = @2))
   ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'Maths',@1=1,@2=N'Calculus'
go

Chú thích Bắt buộc

Chú thích Bắt buộc cho EF biết rằng một thuộc tính cụ thể là bắt buộc. Chúng ta hãy xem lớp Sinh viên sau, trong đó id Bắt buộc được thêm vào thuộc tính FirstMidName. Thuộc tính bắt buộc sẽ buộc EF phải đảm bảo rằng thuộc tính có dữ liệu trong đó.

public class Student {

   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Như đã thấy trong ví dụ trên, thuộc tính Bắt buộc được áp dụng cho FirstMidName và LastName. Vì vậy, Code First sẽ tạo một cột NOT NULL FirstMidName và LastName trong bảng Students như thể hiện trong hình sau.

MaxLength

Thuộc tính MaxLength cho phép bạn chỉ định xác nhận thuộc tính bổ sung. Nó có thể được áp dụng cho thuộc tính kiểu chuỗi hoặc mảng của một lớp miền. EF Code First sẽ đặt kích thước của một cột như được chỉ định trong thuộc tính MaxLength.

Chúng ta hãy xem lớp Khóa học sau, trong đó thuộc tính MaxLength (24) được áp dụng cho thuộc tính Title.

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Khi bạn chạy ứng dụng trên, Code First sẽ tạo một tiêu đề cột nvarchar (24) trong bảng CourseId như thể hiện trong hình sau.

Khi người dùng đặt Tiêu đề chứa hơn 24 ký tự, thì EF sẽ ném EntityValidationError.

Độ dài nhỏ nhất

Thuộc tính MinLength cũng cho phép bạn chỉ định xác nhận thuộc tính bổ sung, giống như bạn đã làm với MaxLength. Thuộc tính MinLength cũng có thể được sử dụng với thuộc tính MaxLength như được hiển thị trong đoạn mã sau.

public class Course {

   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24) , MinLength(5)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

EF sẽ ném EntityValidationError, nếu bạn đặt giá trị của thuộc tính Title nhỏ hơn độ dài được chỉ định trong thuộc tính MinLength hoặc lớn hơn độ dài được chỉ định trong thuộc tính MaxLength.

Chiều dài chuỗi

StringLength cũng cho phép bạn chỉ định các xác nhận thuộc tính bổ sung như MaxLength. Sự khác biệt duy nhất là thuộc tính StringLength chỉ có thể được áp dụng cho một thuộc tính kiểu chuỗi của các lớp Miền.

public class Course {

   public int CourseID { get; set; }
   [StringLength (24)]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Entity Framework cũng xác nhận giá trị của một thuộc tính cho thuộc tính StringLength. Nếu người dùng đặt Tiêu đề chứa nhiều hơn 24 ký tự, thì EF sẽ ném EntityValidationError.

Bàn

Mã mặc định Quy ước đầu tiên tạo ra một tên bảng tương tự như tên lớp. Nếu bạn đang cho phép Code First tạo cơ sở dữ liệu và cũng muốn thay đổi tên của các bảng mà nó đang tạo. Sau đó -

  • Bạn có thể sử dụng Code First với cơ sở dữ liệu hiện có. Nhưng không phải lúc nào tên của các lớp cũng khớp với tên của các bảng trong cơ sở dữ liệu của bạn.

  • Thuộc tính bảng ghi đè quy ước mặc định này.

  • EF Code First sẽ tạo một bảng có tên được chỉ định trong thuộc tính Table cho một lớp miền nhất định.

Hãy xem ví dụ sau trong đó lớp được đặt tên là Sinh viên và theo quy ước, Code First cho rằng điều này sẽ ánh xạ tới một bảng có tên là Sinh viên. Nếu không phải như vậy, bạn có thể chỉ định tên của bảng với thuộc tính Table như được hiển thị trong đoạn mã sau.

[Table("StudentsInfo")]
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bây giờ bạn có thể thấy rằng thuộc tính Table chỉ định bảng là StudentsInfo. Khi bảng được tạo, bạn sẽ thấy tên bảng là StudentsInfo như trong hình sau.

Bạn không thể chỉ định tên bảng mà còn có thể chỉ định một lược đồ cho bảng bằng cách sử dụng thuộc tính Table như được hiển thị trong đoạn mã sau.

[Table("StudentsInfo", Schema = "Admin")] 
public class Student {

   [Key]
   public int StdntID { get; set; }
   [Required]
   public string LastName { get; set; }
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bạn có thể thấy trong ví dụ trên, bảng được chỉ định bằng lược đồ quản trị. Bây giờ Code First sẽ tạo bảng StudentsInfo trong lược đồ Quản trị như thể hiện trong hình sau.

Cột

Nó cũng giống như thuộc tính Table, nhưng thuộc tính Table ghi đè hành vi bảng trong khi thuộc tính Column ghi đè hành vi cột. Mã mặc định Quy ước đầu tiên tạo một tên cột tương tự như tên thuộc tính. Nếu bạn đang cho phép Code First tạo cơ sở dữ liệu và cũng muốn thay đổi tên của các cột trong bảng của bạn. Sau đó -

  • Thuộc tính cột ghi đè quy ước mặc định.

  • EF Code First sẽ tạo một cột có tên được chỉ định trong thuộc tính Column cho một thuộc tính nhất định.

Hãy xem ví dụ sau, trong đó thuộc tính được đặt tên là FirstMidName và theo quy ước, Code First cho rằng điều này sẽ ánh xạ tới một cột có tên FirstMidName.

Nếu không phải như vậy, bạn có thể chỉ định tên của cột bằng thuộc tính Column như được hiển thị trong đoạn mã sau.

public class Student {

   public int ID { get; set; }
   public string LastName { get; set; }
   [Column("FirstName")]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bạn có thể thấy rằng thuộc tính Column chỉ định cột là FirstName. Khi bảng được tạo, bạn sẽ thấy tên cột FirstName như trong hình sau.

Mục lục

Thuộc tính Index đã được giới thiệu trong Entity Framework 6.1. Nếu bạn đang sử dụng phiên bản cũ hơn, thông tin trong phần này không áp dụng.

  • Bạn có thể tạo chỉ mục trên một hoặc nhiều cột bằng IndexAttribute.

  • Việc thêm thuộc tính vào một hoặc nhiều thuộc tính sẽ khiến EF tạo chỉ mục tương ứng trong cơ sở dữ liệu khi nó tạo cơ sở dữ liệu.

  • Các chỉ mục giúp cho việc truy xuất dữ liệu nhanh hơn và hiệu quả, trong hầu hết các trường hợp. Tuy nhiên, việc quá tải một bảng hoặc dạng xem với các chỉ mục có thể ảnh hưởng đến hiệu suất của các hoạt động khác như chèn hoặc cập nhật.

  • Lập chỉ mục là tính năng mới trong Entity Framework nơi bạn có thể cải thiện hiệu suất của ứng dụng Code First của mình bằng cách giảm thời gian cần thiết để truy vấn dữ liệu từ cơ sở dữ liệu.

  • Bạn có thể thêm chỉ mục vào cơ sở dữ liệu của mình bằng cách sử dụng thuộc tính Chỉ mục và ghi đè các cài đặt Duy nhất và Nhóm mặc định để có được chỉ mục phù hợp nhất với tình huống của bạn.

  • Theo mặc định, chỉ mục sẽ được đặt tên là IX_ <tên thuộc tính>

Chúng ta hãy xem đoạn mã sau, trong đó thuộc tính Chỉ mục được thêm vào trong lớp Khóa học cho Tín chỉ.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bạn có thể thấy rằng thuộc tính Chỉ mục được áp dụng cho thuộc tính Tín dụng. Khi bảng được tạo, bạn sẽ thấy IX_Credits trong Chỉ mục.

Theo mặc định, các chỉ mục không phải là duy nhất, nhưng bạn có thể sử dụng IsUniquetham số được đặt tên để chỉ định rằng một chỉ mục phải là duy nhất. Ví dụ sau đây giới thiệu một chỉ mục duy nhất như được hiển thị trong đoạn mã sau.

public class Course {
   public int CourseID { get; set; }
   [Index(IsUnique = true)]
	
   public string Title { get; set; }
   [Index]
	
   public int Credits { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

ForeignKey

Quy ước Code First sẽ xử lý các mối quan hệ phổ biến nhất trong mô hình của bạn, nhưng có một số trường hợp cần trợ giúp. Ví dụ: bằng cách thay đổi tên của thuộc tính khóa trong lớp Sinh viên đã tạo ra một vấn đề với mối quan hệ của nó với lớp Đăng ký.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Trong khi tạo cơ sở dữ liệu, Code First nhìn thấy thuộc tính StudentID trong lớp Enrollment và nhận dạng nó, theo quy ước rằng nó khớp với tên lớp cộng với “ID”, làm khóa ngoại cho lớp Student. Tuy nhiên, không có thuộc tính StudentID trong lớp Sinh viên, nhưng thuộc tính StdntID là lớp Sinh viên.

Giải pháp cho việc này là tạo thuộc tính điều hướng trong Enrollment và sử dụng ForeignKey DataAnnotation để giúp Code First hiểu cách xây dựng mối quan hệ giữa hai lớp như được hiển thị trong đoạn mã sau.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
	
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
   [ForeignKey("StudentID")]
	
   public virtual Student Student { get; set; }
}

Bây giờ bạn có thể thấy rằng thuộc tính ForeignKey được áp dụng cho thuộc tính điều hướng.

NotMapped

Theo quy ước mặc định của Code First, mọi thuộc tính thuộc kiểu dữ liệu được hỗ trợ và bao gồm getters và setters đều được thể hiện trong cơ sở dữ liệu. Nhưng điều này không phải lúc nào cũng đúng trong các ứng dụng của bạn. Thuộc tính NotMapped ghi đè quy ước mặc định này. Ví dụ: bạn có thể có một thuộc tính trong lớp Sinh viên như FatherName, nhưng nó không cần phải được lưu trữ. Bạn có thể áp dụng thuộc tính NotMapped cho thuộc tính FatherName mà bạn không muốn tạo một cột trong cơ sở dữ liệu như được hiển thị trong đoạn mã sau.

public class Student {
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
	
   public DateTime EnrollmentDate { get; set; }
   [NotMapped]

   public int FatherName { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Bạn có thể thấy rằng thuộc tính NotMapped được áp dụng cho thuộc tính FatherName. Khi bảng được tạo, bạn sẽ thấy rằng cột FatherName sẽ không được tạo trong cơ sở dữ liệu, nhưng nó hiện diện trong lớp Sinh viên.

Code First sẽ không tạo một cột cho thuộc tính, không có cột hoặc bộ định tuyến như được hiển thị trong ví dụ sau về thuộc tính Địa chỉ và Tuổi của lớp Sinh viên.

InverseProperty

InverseProperty được sử dụng khi bạn có nhiều mối quan hệ giữa các lớp. Trong lớp Ghi danh, bạn có thể muốn theo dõi những ai đã đăng ký Khóa học hiện tại và Khóa học trước đó. Hãy thêm hai thuộc tính điều hướng cho lớp Enrollment.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course CurrCourse { get; set; }
   public virtual Course PrevCourse { get; set; }
   public virtual Student Student { get; set; }
}

Tương tự, bạn cũng sẽ cần thêm lớp Khóa học được tham chiếu bởi các thuộc tính này. Lớp Khóa học có các thuộc tính điều hướng trở lại Lớp đăng ký, lớp này chứa tất cả các đăng ký hiện tại và trước đó.

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

Code First tạo cột khóa ngoại {Class Name} _ {Primary Key}, nếu thuộc tính khóa ngoại không được bao gồm trong một lớp cụ thể như được hiển thị trong các lớp trên. Khi cơ sở dữ liệu được tạo, bạn sẽ thấy các khóa ngoại sau.

Như bạn có thể thấy rằng Code đầu tiên không thể tự khớp các thuộc tính trong hai lớp. Bảng cơ sở dữ liệu cho Đăng ký phải có một khóa ngoại cho CurrCourse và một cho PrevCourse, nhưng Code First sẽ tạo bốn thuộc tính khóa ngoại, tức là

  • CurrCourse _CourseID
  • PrevCourse _CourseID
  • Course_CourseID và
  • Course_CourseID1

Để khắc phục những sự cố này, bạn có thể sử dụng chú thích InverseProperty để chỉ định căn chỉnh của các thuộc tính.

public class Course {

   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   [InverseProperty("CurrCourse")]

   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   [InverseProperty("PrevCourse")]

   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

Như bạn có thể thấy thuộc tính InverseProperty được áp dụng trong lớp Khóa học ở trên bằng cách chỉ định thuộc tính tham chiếu nào của lớp Đăng ký. Bây giờ, Code First sẽ tạo một cơ sở dữ liệu và chỉ tạo hai cột khóa ngoại trong bảng Enrollments như thể hiện trong hình sau.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Fluent API là một cách nâng cao để chỉ định cấu hình mô hình bao gồm mọi thứ mà chú thích dữ liệu có thể thực hiện ngoài một số cấu hình nâng cao hơn không thể thực hiện với chú thích dữ liệu. Chú thích dữ liệu và API thông thạo có thể được sử dụng cùng nhau, nhưng Code First ưu tiên cho API thông thạo> chú thích dữ liệu> quy ước mặc định.

  • API thông thạo là một cách khác để định cấu hình các lớp miền của bạn.

  • Code First Fluent API thường được truy cập nhiều nhất bằng cách ghi đè phương thức OnModelCreating trên DbContext dẫn xuất của bạn.

  • API Fluent cung cấp nhiều chức năng hơn cho cấu hình so với DataAnnotations. API Fluent hỗ trợ các loại ánh xạ sau.

Trong chương này, chúng ta sẽ tiếp tục với ví dụ đơn giản chứa các lớp Sinh viên, Khóa học và Ghi danh và một lớp ngữ cảnh có tên MyContext như được hiển thị trong đoạn mã sau.

using System.Data.Entity; 
using System.Linq; 
using System.Text;
using System.Threading.Tasks;  

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }
   
   public enum Grade {
      A, B, C, D, F
   }

   public class Enrollment {
      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Grade? Grade { get; set; }
		
      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
		
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Để truy cập API Fluent, bạn cần ghi đè phương thức OnModelCreating trong DbContext. Hãy xem một ví dụ đơn giản, trong đó chúng tôi sẽ đổi tên cột trong bảng sinh viên từ FirstMidName thành FirstName như được hiển thị trong đoạn mã sau.

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
      .HasColumnName("FirstName");}

      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
}

DbModelBuilder được sử dụng để ánh xạ các lớp CLR vào một lược đồ cơ sở dữ liệu. Nó là lớp chính và trên đó bạn có thể cấu hình tất cả các lớp miền của mình. Cách tiếp cận tập trung vào mã này để xây dựng Mô hình dữ liệu thực thể (EDM) được gọi là Mã đầu tiên.

Fluent API cung cấp một số phương pháp quan trọng để định cấu hình các thực thể và thuộc tính của nó để ghi đè các quy ước Code First khác nhau. Dưới đây là một số trong số chúng.

Sơ không. Tên & Mô tả phương pháp
1

ComplexType<TComplexType>

Đăng ký một kiểu như một kiểu phức hợp trong mô hình và trả về một đối tượng có thể được sử dụng để cấu hình kiểu phức tạp. Phương thức này có thể được gọi nhiều lần cho cùng một kiểu để thực hiện nhiều dòng cấu hình.

2

Entity<TEntityType>

Đăng ký một loại thực thể như một phần của mô hình và trả về một đối tượng có thể được sử dụng để định cấu hình thực thể. Phương thức này có thể được gọi nhiều lần cho cùng một thực thể để thực hiện nhiều dòng cấu hình.

3

HasKey<TKey>

Định cấu hình (các) thuộc tính khóa chính cho loại thực thể này.

4

HasMany<TTargetEntity>

Định cấu hình nhiều mối quan hệ từ loại thực thể này.

5

HasOptional<TTargetEntity>

Định cấu hình mối quan hệ tùy chọn từ loại thực thể này. Các phiên bản của kiểu thực thể sẽ có thể được lưu vào cơ sở dữ liệu mà không cần chỉ định mối quan hệ này. Khoá ngoại trong cơ sở dữ liệu sẽ không có giá trị.

6

HasRequired<TTargetEntity>

Định cấu hình mối quan hệ bắt buộc từ loại thực thể này. Các phiên bản của loại thực thể sẽ không thể được lưu vào cơ sở dữ liệu trừ khi mối quan hệ này được chỉ định. Khóa ngoại trong cơ sở dữ liệu sẽ không thể null.

7

Ignore<TProperty>

Loại trừ một thuộc tính khỏi mô hình để nó sẽ không được ánh xạ tới cơ sở dữ liệu. (Được kế thừa từ StructuralTypeConfiguration <TStructuralType>)

số 8

Property<T>

Định cấu hình thuộc tính struct được xác định trên loại này. (Được kế thừa từ StructuralTypeConfiguration <TStructuralType>)

9

ToTable(String)

Định cấu hình tên bảng mà loại thực thể này được ánh xạ tới.

Fluent API cho phép bạn định cấu hình các thực thể của mình hoặc thuộc tính của chúng, cho dù bạn muốn thay đổi điều gì đó về cách chúng ánh xạ đến cơ sở dữ liệu hoặc cách chúng liên quan với nhau. Có rất nhiều ánh xạ và mô hình hóa mà bạn có thể tác động bằng cách sử dụng các cấu hình. Sau đây là các loại ánh xạ chính mà API Fluent hỗ trợ:

  • Bản đồ thực thể
  • Bản đồ thuộc tính

Bản đồ thực thể

Ánh xạ thực thể chỉ là một số ánh xạ đơn giản sẽ tác động đến sự hiểu biết của Entity Framework về cách các lớp được ánh xạ tới cơ sở dữ liệu. Tất cả những điều này chúng ta đã thảo luận trong phần chú thích dữ liệu và ở đây chúng ta sẽ xem cách đạt được những điều tương tự bằng cách sử dụng Fluent API.

  • Vì vậy, thay vì đi vào các lớp miền để thêm các cấu hình này, chúng ta có thể thực hiện việc này bên trong ngữ cảnh.

  • Điều đầu tiên là ghi đè phương thức OnModelCreating, phương thức này cho phép modelBuilder hoạt động.

Lược đồ mặc định

Lược đồ mặc định là dbo khi cơ sở dữ liệu được tạo. Bạn có thể sử dụng phương thức HasDefaultSchema trên DbModelBuilder để chỉ định lược đồ cơ sở dữ liệu để sử dụng cho tất cả các bảng, thủ tục được lưu trữ, v.v.

Hãy xem ví dụ sau, trong đó lược đồ quản trị được áp dụng.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      //Configure default schema
      modelBuilder.HasDefaultSchema("Admin");
   }
	
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Ánh xạ thực thể vào bảng

Với quy ước mặc định, Code First sẽ tạo các bảng cơ sở dữ liệu với tên của các thuộc tính DbSet trong lớp ngữ cảnh như Khóa học, Đăng ký và Sinh viên. Nhưng nếu bạn muốn các tên bảng khác nhau thì bạn có thể ghi đè quy ước này và có thể cung cấp tên bảng khác với thuộc tính DbSet, như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   //Map entity to table
   modelBuilder.Entity<Student>().ToTable("StudentData");
   modelBuilder.Entity<Course>().ToTable("CourseDetail");
   modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}

Khi cơ sở dữ liệu được tạo, bạn sẽ thấy tên bảng như được chỉ định trong phương thức OnModelCreating.

Tách đối tượng (Đối tượng ánh xạ thành nhiều bảng)

Thực thể Tách cho phép bạn kết hợp dữ liệu đến từ nhiều bảng thành một lớp duy nhất và nó chỉ có thể được sử dụng với các bảng có mối quan hệ một-một giữa chúng. Hãy xem ví dụ sau, trong đó thông tin Sinh viên được ánh xạ thành hai bảng.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   //Map entity to table
   modelBuilder.Entity<Student>().Map(sd ⇒ {
      sd.Properties(p ⇒ new { p.ID, p.FirstMidName, p.LastName });
      sd.ToTable("StudentData");
   })

   .Map(si ⇒ {
      si.Properties(p ⇒ new { p.ID, p.EnrollmentDate });
      si.ToTable("StudentEnrollmentInfo");
   });

   modelBuilder.Entity<Course>().ToTable("CourseDetail");
   modelBuilder.Entity<Enrollment>().ToTable("EnrollmentInfo");
}

Trong đoạn mã trên, bạn có thể thấy thực thể Student được chia thành hai bảng sau bằng cách ánh xạ một số thuộc tính vào bảng StudentData và một số thuộc tính vào bảng StudentEnrollmentInfo bằng phương pháp Map.

  • StudentData - Chứa FirstMidName và Last Name của học sinh.

  • StudentEnrollmentInfo - Chứa Ngày đăng ký.

Khi cơ sở dữ liệu được tạo, bạn sẽ thấy các bảng sau trong cơ sở dữ liệu của mình như được hiển thị trong hình ảnh sau.

Bản đồ thuộc tính

Phương thức Thuộc tính được sử dụng để định cấu hình các thuộc tính cho từng thuộc tính thuộc một thực thể hoặc kiểu phức hợp. Phương thức Thuộc tính được sử dụng để lấy một đối tượng cấu hình cho một thuộc tính nhất định. Bạn cũng có thể ánh xạ và định cấu hình các thuộc tính của các lớp miền của mình bằng API Fluent.

Định cấu hình khóa chính

Quy ước mặc định cho các khóa chính là:

  • Lớp xác định một thuộc tính có tên là “ID” hoặc “Id”
  • Tên lớp theo sau là “ID” hoặc “Id”

Nếu lớp của bạn không tuân theo các quy ước mặc định cho khóa chính như được hiển thị trong mã sau của lớp Sinh viên -

public class Student {
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Sau đó, để đặt rõ ràng một thuộc tính làm khóa chính, bạn có thể sử dụng phương thức HasKey như được hiển thị trong đoạn mã sau:

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");
	
   // Configure Primary Key
   modelBuilder.Entity<Student>().HasKey<int>(s ⇒ s.StdntID); 
}

Định cấu hình cột

Trong Entity Framework, theo mặc định Code First sẽ tạo một cột cho thuộc tính có cùng tên, thứ tự và kiểu dữ liệu. Nhưng bạn cũng có thể ghi đè quy ước này, như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   //Configure EnrollmentDate Column
   modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate)
	
   .HasColumnName("EnDate")
   .HasColumnType("DateTime")
   .HasColumnOrder(2);
}

Định cấu hình thuộc tính MaxLength

Trong ví dụ sau, thuộc tính Tiêu đề khóa học không được dài hơn 24 ký tự. Khi người dùng chỉ định giá trị dài hơn 24 ký tự, thì người dùng sẽ nhận được một ngoại lệ DbEntityValidationException.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");
   modelBuilder.Entity<Course>().Property(p ⇒ p.Title).HasMaxLength(24);
}

Định cấu hình thuộc tính Null hoặc NotNull

Trong ví dụ sau, thuộc tính Tiêu đề khóa học là bắt buộc nên phương thức IsRequired được sử dụng để tạo cột NotNull. Tương tự, Student EnrollmentDate là tùy chọn nên chúng tôi sẽ sử dụng phương pháp IsOptional để cho phép giá trị null trong cột này như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");
   modelBuilder.Entity<Course>().Property(p ⇒ p.Title).IsRequired();
   modelBuilder.Entity<Student>().Property(p ⇒ p.EnrollmentDate).IsOptional();
	
   //modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
   //.HasColumnName("FirstName"); 
}

Định cấu hình mối quan hệ

Mối quan hệ, trong ngữ cảnh của cơ sở dữ liệu, là một tình huống tồn tại giữa hai bảng cơ sở dữ liệu quan hệ, khi một bảng có khóa ngoại tham chiếu đến khóa chính của bảng kia. Khi làm việc với Code First, bạn xác định mô hình của mình bằng cách xác định các lớp CLR miền của bạn. Theo mặc định, Entity Framework sử dụng quy ước Code First để ánh xạ các lớp của bạn với lược đồ cơ sở dữ liệu.

  • Nếu bạn sử dụng quy ước đặt tên Code First, trong hầu hết các trường hợp, bạn có thể dựa vào Code First để thiết lập mối quan hệ giữa các bảng của mình dựa trên các khóa ngoại và thuộc tính điều hướng.

  • Nếu chúng không đáp ứng các quy ước đó, cũng có những cấu hình bạn có thể sử dụng để tác động đến mối quan hệ giữa các lớp và cách các mối quan hệ đó được thực hiện trong cơ sở dữ liệu khi bạn thêm cấu hình trong Code First.

  • Một số trong số đó có sẵn trong chú thích dữ liệu và bạn có thể áp dụng một số cách phức tạp hơn với API Fluent.

Định cấu hình mối quan hệ một-một

Khi bạn xác định mối quan hệ một-một trong mô hình của mình, bạn sử dụng thuộc tính điều hướng tham chiếu trong mỗi lớp. Trong cơ sở dữ liệu, cả hai bảng chỉ có thể có một bản ghi ở hai bên của mối quan hệ. Mỗi giá trị khóa chính chỉ liên quan đến một bản ghi (hoặc không có bản ghi) trong bảng liên quan.

  • Mối quan hệ một-một được tạo nếu cả hai cột liên quan đều là khóa chính hoặc có các ràng buộc duy nhất.

  • Trong mối quan hệ một-một, khóa chính hoạt động bổ sung như một khóa ngoại và không có cột khóa ngoại riêng biệt cho cả hai bảng.

  • Kiểu quan hệ này không phổ biến vì hầu hết thông tin liên quan theo cách này sẽ nằm trong một bảng.

Hãy xem ví dụ sau, nơi chúng ta sẽ thêm một lớp khác vào mô hình của mình để tạo mối quan hệ một-một.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual StudentLogIn StudentLogIn { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }
	
   public virtual Student Student { get; set; }
}

Như bạn có thể thấy trong đoạn mã trên, các thuộc tính Key và ForeignKey được sử dụng cho thuộc tính ID trong lớp StudentLogIn, để đánh dấu nó là Khóa chính cũng như Khóa ngoại.

Để định cấu hình mối quan hệ một-không hoặc một giữa Student và StudentLogIn bằng cách sử dụng Fluent API, bạn cần ghi đè phương thức OnModelCreating như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   // Configure ID as PK for StudentLogIn
   modelBuilder.Entity<StudentLogIn>()
   .HasKey(s ⇒ s.ID);

   // Configure ID as FK for StudentLogIn
   modelBuilder.Entity<Student>()
   
   .HasOptional(s ⇒ s.StudentLogIn) //StudentLogIn is optional
   .WithRequired(t ⇒ t.Student); // Create inverse relationship
}

Trong hầu hết các trường hợp, Khung thực thể có thể suy ra loại nào là loại phụ thuộc và loại nào là chính trong mối quan hệ. Tuy nhiên, khi cả hai đầu mối quan hệ là bắt buộc hoặc cả hai bên đều không bắt buộc thì Khung thực thể không thể xác định người phụ thuộc và người chính. Khi cả hai kết thúc mối quan hệ đều được yêu cầu, bạn có thể sử dụng HasRequired như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   // Configure ID as PK for StudentLogIn
   modelBuilder.Entity<StudentLogIn>()
   .HasKey(s ⇒ s.ID);

   // Configure ID as FK for StudentLogIn
   modelBuilder.Entity<Student>()
   .HasRequired(r ⇒ r.Student)
   .WithOptional(s ⇒ s.StudentLogIn);  
}

Khi cơ sở dữ liệu được tạo, bạn sẽ thấy mối quan hệ đó được tạo như thể hiện trong hình sau.

Định cấu hình mối quan hệ một-nhiều

Bảng khóa chính chỉ chứa một bản ghi liên quan đến không, một hoặc nhiều bản ghi trong bảng liên quan. Đây là kiểu quan hệ được sử dụng phổ biến nhất.

  • Trong kiểu quan hệ này, một hàng trong bảng A có thể có nhiều hàng phù hợp trong bảng B, nhưng một hàng trong bảng B chỉ có thể có một hàng phù hợp trong bảng A.

  • Khóa ngoại được định nghĩa trên bảng đại diện cho nhiều phần cuối của mối quan hệ.

  • Ví dụ, trong sơ đồ trên, bảng Student và Enrollment có mối quan hệ một bên, mỗi học sinh có thể có nhiều đăng ký, nhưng mỗi ghi danh chỉ thuộc về một học sinh.

Dưới đây là Sinh viên và Đăng ký có mối quan hệ một-nhiều, nhưng khóa ngoại trong bảng Đăng ký không tuân theo các quy ước mặc định về Mã đầu tiên.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
	
   //StdntID is not following code first conventions name
   public int StdntID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual StudentLogIn StudentLogIn { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Trong trường hợp này, để định cấu hình mối quan hệ một-nhiều bằng Fluent API, bạn cần sử dụng phương thức HasForeignKey như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   //Configure FK for one-to-many relationship
   modelBuilder.Entity<Enrollment>()

   .HasRequired<Student>(s ⇒ s.Student)
   .WithMany(t ⇒ t.Enrollments)
   .HasForeignKey(u ⇒ u.StdntID);  
}

Khi cơ sở dữ liệu được tạo, bạn sẽ thấy rằng mối quan hệ được tạo như thể hiện trong hình sau.

Trong ví dụ trên, phương thức HasRequired chỉ định rằng thuộc tính điều hướng Sinh viên phải là Null. Vì vậy, bạn phải chỉ định thực thể Student with Enrollment mỗi khi bạn thêm hoặc cập nhật Enrollment. Để xử lý điều này, chúng ta cần sử dụng phương thức HasOptional thay vì phương thức HasRequired.

Định cấu hình mối quan hệ nhiều-nhiều

Mỗi bản ghi trong cả hai bảng có thể liên quan đến bất kỳ số lượng bản ghi nào (hoặc không có bản ghi) trong bảng khác.

  • Bạn có thể tạo một mối quan hệ như vậy bằng cách xác định bảng thứ ba, được gọi là bảng nối, có khóa chính bao gồm các khóa ngoại từ cả bảng A và bảng B.

  • Ví dụ, bảng Sinh viên và bảng Khóa học có mối quan hệ nhiều-nhiều.

Sau đây là các lớp Sinh viên và Khóa học trong đó Sinh viên và Khóa học có mối quan hệ nhiều bên, bởi vì cả hai lớp đều có thuộc tính điều hướng Sinh viên và Khóa học là tập hợp. Nói cách khác, một thực thể này có một tập hợp thực thể khác.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Course> Courses { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Student> Students { get; set; }
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Để định cấu hình mối quan hệ nhiều-nhiều giữa Sinh viên và Khóa học, bạn có thể sử dụng API Fluent như được hiển thị trong đoạn mã sau.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   // Configure many-to-many relationship
   modelBuilder.Entity<Student>()
   .HasMany(s ⇒ s.Courses) 
   .WithMany(s ⇒ s.Students);
}

Quy ước Code First mặc định được sử dụng để tạo một bảng nối khi cơ sở dữ liệu được tạo. Kết quả là bảng StudentCourses được tạo với các cột Course_CourseID và Student_ID như được hiển thị trong hình sau.

Nếu bạn muốn chỉ định tên bảng nối và tên của các cột trong bảng, bạn cần thực hiện cấu hình bổ sung bằng cách sử dụng phương pháp Bản đồ.

protected override void OnModelCreating(DbModelBuilder modelBuilder) {

   //Configure default schema
   modelBuilder.HasDefaultSchema("Admin");

   // Configure many-to-many relationship 
   modelBuilder.Entity<Student>()

   .HasMany(s ⇒ s.Courses)
   .WithMany(s ⇒ s.Students)
   
   .Map(m ⇒ {
      m.ToTable("StudentCoursesTable");
      m.MapLeftKey("StudentID");
      m.MapRightKey("CourseID");
   }); 
}

Bạn có thể thấy khi cơ sở dữ liệu được tạo, tên bảng và cột được tạo như được chỉ định trong đoạn mã trên.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong Entity Framework, Seed đã được giới thiệu trong EF 4.1 và hoạt động với các trình khởi tạo cơ sở dữ liệu. Ý tưởng chung của mộtSeed Methodlà khởi tạo dữ liệu vào một cơ sở dữ liệu đang được tạo bởi Code First hoặc được phát triển bởi Migrations. Dữ liệu này thường là dữ liệu kiểm tra, nhưng cũng có thể là dữ liệu tham chiếu như danh sách Sinh viên, Khóa học đã biết, v.v. Khi dữ liệu được khởi tạo, nó thực hiện như sau:

  • Kiểm tra xem cơ sở dữ liệu đích đã tồn tại hay chưa.
  • Nếu đúng, thì mô hình Code First hiện tại được so sánh với mô hình được lưu trữ trong siêu dữ liệu trong cơ sở dữ liệu.
  • Cơ sở dữ liệu bị loại bỏ nếu mô hình hiện tại không khớp với mô hình trong cơ sở dữ liệu.
  • Cơ sở dữ liệu được tạo nếu nó bị loại bỏ hoặc không tồn tại ngay từ đầu.
  • Nếu cơ sở dữ liệu đã được tạo, thì phương thức Seed khởi tạo sẽ được gọi.

Phương thức Seed lấy đối tượng ngữ cảnh cơ sở dữ liệu làm tham số đầu vào và mã trong phương thức sử dụng đối tượng đó để thêm các thực thể mới vào cơ sở dữ liệu. Để gieo dữ liệu vào cơ sở dữ liệu của bạn, bạn cần ghi đè phương thức Seed. Hãy xem ví dụ sau, trong đó một số dữ liệu mặc định được khởi tạo vào cơ sở dữ liệu trong một lớp nội bộ.

private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {

   protected override void Seed(MyContext context) {

      IList<Student> students = new List<Student>();

      students.Add(new Student() {
         FirstMidName = "Andrew", 
         LastName = "Peters", 
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      students.Add(new Student() {
         FirstMidName = "Brice", 
         LastName = "Lambson", 
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      students.Add(new Student() {
         FirstMidName = "Rowan", 
         LastName = "Miller", 
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      });

      foreach (Student student in students)
      context.Students.Add(student);
      base.Seed(context);
   }
}

Trong đoạn mã trên, bảng sinh viên được khởi tạo. Bạn cần đặt lớp trình khởi tạo DB này trong lớp ngữ cảnh như được hiển thị trong đoạn mã sau.

public MyContext() : base("name=MyContextDB") {
   Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
}

Sau đây là việc triển khai lớp hoàn chỉnh của lớp MyContext, lớp này cũng chứa lớp bộ khởi tạo DB.

public class MyContext : DbContext {

   public MyContext() : base("name=MyContextDB") {
      Database.SetInitializer<MyContext>(new UniDBInitializer<MyContext>());
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
	
   private class UniDBInitializer<T> : DropCreateDatabaseAlways<MyContext> {

      protected override void Seed(MyContext context) {

         IList<Student> students = new List<Student>();
			
         students.Add(new Student() {
            FirstMidName = "Andrew", 
            LastName = "Peters", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString()) 
         });

         students.Add(new Student() {
            FirstMidName = "Brice", 
            LastName = "Lambson", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         students.Add(new Student() {
            FirstMidName = "Rowan", 
            LastName = "Miller", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         });

         foreach (Student student in students)
         context.Students.Add(student);
         base.Seed(context);
      }
   } 
}

Khi ví dụ trên được biên dịch và thực thi, bạn có thể thấy dữ liệu trong cơ sở dữ liệu như thể hiện trong hình sau.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Entity Framework 4.3 bao gồm một tính năng Code First Migrations mới cho phép bạn từng bước phát triển lược đồ cơ sở dữ liệu khi mô hình của bạn thay đổi theo thời gian. Đối với hầu hết các nhà phát triển, đây là một cải tiến lớn so với các tùy chọn khởi tạo cơ sở dữ liệu từ các phiên bản 4.1 và 4.2 yêu cầu bạn cập nhật cơ sở dữ liệu theo cách thủ công hoặc thả và tạo lại nó khi mô hình của bạn thay đổi.

  • Trước Entity Framework 4.3, nếu bạn đã có dữ liệu (ngoài dữ liệu gốc) hoặc các thủ tục, trình kích hoạt được lưu trữ, v.v. trong cơ sở dữ liệu của mình, thì các chiến lược này được sử dụng để loại bỏ toàn bộ cơ sở dữ liệu và tạo lại nó, vì vậy bạn sẽ mất dữ liệu và các DB khác các đối tượng.

  • Với việc di chuyển, nó sẽ tự động cập nhật giản đồ cơ sở dữ liệu, khi mô hình của bạn thay đổi mà không làm mất bất kỳ dữ liệu hiện có nào hoặc các đối tượng cơ sở dữ liệu khác.

  • Nó sử dụng một trình khởi tạo cơ sở dữ liệu mới có tên MigrateDatabaseToLatestVersion.

Có hai loại Di cư -

  • Di chuyển tự động
  • Di chuyển dựa trên mã

Di chuyển tự động

Di chuyển tự động lần đầu tiên được giới thiệu trong Entity framework 4.3. Trong quá trình di chuyển tự động, bạn không cần phải xử lý quá trình di chuyển cơ sở dữ liệu theo cách thủ công trong tệp mã. Ví dụ: đối với mỗi thay đổi, bạn cũng sẽ cần thay đổi trong các lớp miền của mình. Nhưng với di chuyển tự động, bạn chỉ cần chạy một lệnh trong Bảng điều khiển Trình quản lý Gói để thực hiện việc này.

Hãy cùng xem quy trình di chuyển tự động từng bước sau đây.

Khi bạn sử dụng cách tiếp cận Code First, bạn không có cơ sở dữ liệu cho ứng dụng của mình.

Trong ví dụ này, chúng ta sẽ bắt đầu với 3 lớp cơ bản của chúng ta như Sinh viên, Khóa học và Ghi danh như được hiển thị trong đoạn mã sau.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Sau đây là lớp ngữ cảnh.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Trước khi chạy ứng dụng, bạn cần bật tính năng di chuyển tự động.

Step 1 - Mở Bảng điều khiển Trình quản lý gói từ Công cụ → Trình quản lý gói NuGet → Bảng điều khiển Trình quản lý gói.

Step 2 - Để kích hoạt di chuyển tự động, hãy chạy lệnh sau trong Bảng điều khiển Trình quản lý Gói.

PM> enable-migrations -EnableAutomaticMigrations:$true

Step 3 - Khi lệnh chạy thành công, nó sẽ tạo một lớp Cấu hình được niêm phong nội bộ trong thư mục Di chuyển của dự án của bạn như được hiển thị trong đoạn mã sau.

namespace EFCodeFirstDemo.Migrations {

   using System;
   using System.Data.Entity;
   using System.Data.Entity.Migrations;
   using System.Linq;
	
   internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {

      public Configuration() {
         AutomaticMigrationsEnabled = true;
         ContextKey = "EFCodeFirstDemo.MyContext";
      }

      protected override void Seed(EFCodeFirstDemo.MyContext context) {

         //  This method will be called after migrating to the latest version.
         //  You can use the DbSet<T>.AddOrUpdate() helper extension method
         //  to avoid creating duplicate seed data. E.g.

         //  context.People.AddOrUpdate(
            //  p ⇒ p.FullName, 
            //  new Person { FullName = "Andrew Peters" }, 
            //  new Person { FullName = "Brice Lambson" }, 
            //  new Person { FullName = "Rowan Miller" }
         //  );
      }
   }
}

Step 4 - Đặt trình khởi tạo cơ sở dữ liệu trong lớp ngữ cảnh với chiến lược khởi tạo DB mới MigrateDatabaseToLatestVersion.

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, 
         EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

Step 5- Bạn đã thiết lập di chuyển tự động. Khi bạn thực thi ứng dụng của mình, ứng dụng sẽ tự động thực hiện quá trình di chuyển khi bạn thay đổi mô hình.

Step 6- Như bạn có thể thấy rằng một bảng hệ thống __MigrationHistory cũng được tạo trong cơ sở dữ liệu của bạn với các bảng khác. Trong __MigrationHistory, di chuyển tự động duy trì lịch sử thay đổi cơ sở dữ liệu.

Step 7- Khi bạn thêm một lớp thực thể khác làm lớp miền và thực thi ứng dụng của bạn, thì nó sẽ tạo bảng trong cơ sở dữ liệu của bạn. Hãy thêm lớp StudentLogIn sau.

public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }
	
   public virtual Student Student { get; set; }
}

Step 8 - Đừng quên thêm DBSet cho lớp được đề cập ở trên trong lớp ngữ cảnh của bạn như được hiển thị trong đoạn mã sau.

public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }

Step 9 - Chạy lại ứng dụng của bạn và bạn sẽ thấy rằng bảng StudentsLogIn được thêm vào cơ sở dữ liệu của bạn.

Các bước được đề cập ở trên để di chuyển tự động sẽ chỉ hoạt động đối với thực thể của bạn. Ví dụ: để thêm một lớp thực thể khác hoặc xóa lớp thực thể hiện có, nó sẽ di chuyển thành công. Nhưng nếu bạn thêm hoặc xóa bất kỳ thuộc tính nào vào lớp thực thể của mình thì nó sẽ ném ra một ngoại lệ.

Step 10 - Để xử lý việc di chuyển thuộc tính, bạn cần đặt AutomaticMigrationDataLossAllowed = true trong phương thức khởi tạo lớp cấu hình.

public Configuration() {
   AutomaticMigrationsEnabled = true;
   AutomaticMigrationDataLossAllowed = true;
   ContextKey = "EFCodeFirstDemo.MyContext";
}

Di chuyển dựa trên mã

Khi bạn phát triển một ứng dụng mới, mô hình dữ liệu của bạn thay đổi thường xuyên và mỗi khi mô hình thay đổi, nó sẽ không đồng bộ với cơ sở dữ liệu. Bạn đã định cấu hình Khung thực thể để tự động thả và tạo lại cơ sở dữ liệu mỗi khi bạn thay đổi mô hình dữ liệu. Di chuyển dựa trên mã rất hữu ích khi bạn muốn kiểm soát nhiều hơn việc di chuyển.

  • Khi bạn thêm, xóa hoặc thay đổi các lớp thực thể hoặc thay đổi lớp DbContext của mình, lần sau khi bạn chạy ứng dụng, nó sẽ tự động xóa cơ sở dữ liệu hiện có của bạn, tạo một cơ sở dữ liệu mới phù hợp với mô hình và gieo nó bằng dữ liệu thử nghiệm.

  • Tính năng Code First Migrations giải quyết vấn đề này bằng cách cho phép Code First cập nhật giản đồ cơ sở dữ liệu thay vì xóa và tạo lại cơ sở dữ liệu. Để triển khai ứng dụng, bạn sẽ phải bật Migrations.

Đây là quy tắc cơ bản để di chuyển các thay đổi trong cơ sở dữ liệu -

  • Bật di chuyển
  • Thêm di chuyển
  • Cập nhật cơ sở dữ liệu

Hãy xem quy trình từng bước của quá trình di chuyển cơ sở mã sau đây.

Khi bạn sử dụng cách tiếp cận mã đầu tiên, bạn không có cơ sở dữ liệu cho ứng dụng của mình.

Trong ví dụ này, chúng tôi sẽ bắt đầu lại với 3 lớp cơ bản của chúng tôi như Sinh viên, Khóa học và Ghi danh như được hiển thị trong đoạn mã sau.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Sau đây là lớp ngữ cảnh.

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<
         MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

Step 1 - Trước khi chạy ứng dụng, bạn cần kích hoạt tính năng di chuyển.

Step 2 - Mở Bảng điều khiển Trình quản lý Gói từ Công cụ → Trình quản lý gói NuGet → Bảng điều khiển Trình quản lý gói.

Step 3 - Di chuyển đã được bật, bây giờ hãy thêm di chuyển trong ứng dụng của bạn bằng cách thực hiện lệnh sau.

PM> add-migration "UniDB Schema"

Step 4 - Khi lệnh được thực hiện thành công, bạn sẽ thấy một tệp mới đã được tạo trong thư mục Migration với tên của tham số bạn đã truyền vào lệnh với tiền tố dấu thời gian như trong hình sau.

Step 5 - Bạn có thể tạo hoặc cập nhật cơ sở dữ liệu bằng lệnh “update-database”.

PM> Update-Database -Verbose

Cờ "-Verbose" chỉ định hiển thị các Câu lệnh SQL đang được áp dụng cho cơ sở dữ liệu đích trong bảng điều khiển.

Step 6 - Hãy thêm một thuộc tính nữa 'Tuổi' trong lớp sinh viên và sau đó thực hiện câu lệnh cập nhật.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public int Age { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Khi bạn thực thi PM → Update-Database –Verbose, khi lệnh được thực thi thành công, bạn sẽ thấy cột Age mới được thêm vào cơ sở dữ liệu của bạn.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trong chương này, chúng ta sẽ học cách di chuyển các thay đổi vào cơ sở dữ liệu khi có nhiều lớp DbContext trong ứng dụng.

  • Nhiều DbContext lần đầu tiên được giới thiệu trong Entity Framework 6.0.
  • Nhiều lớp ngữ cảnh có thể thuộc về một cơ sở dữ liệu hoặc hai cơ sở dữ liệu khác nhau.

Trong ví dụ của chúng tôi, chúng tôi sẽ xác định hai lớp Ngữ cảnh cho cùng một cơ sở dữ liệu. Trong đoạn mã sau, có hai lớp DbContext dành cho Sinh viên và Giáo viên.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
}

public class MyStudentContext : DbContext {
   public MyStudentContext() : base("UniContextDB") {}
   public virtual DbSet<Student> Students { get; set; }
}

public class Teacher {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime HireDate { get; set; }
}

public class MyTeacherContext : DbContext {
   public MyTeacherContext() : base("UniContextDB") {}
   public virtual DbSet<Teacher> Teachers { get; set; }
}

Như bạn có thể thấy trong đoạn mã trên, có hai mô hình được gọi là “Sinh viên” và “Giáo viên”. Mỗi người được liên kết với một lớp ngữ cảnh tương ứng cụ thể, tức là Sinh viên được liên kết với MyStudentContext và Giáo viên được liên kết với MyTeacherContext.

Đây là quy tắc cơ bản để di chuyển các thay đổi trong cơ sở dữ liệu, khi có nhiều lớp Ngữ cảnh trong cùng một dự án.

  • enable-migrations -ContextTypeName <DbContext-Name-with-Namespaces> MigrationsDirectory: <Migrations-Directory-Name>

  • Add-Migration -configuration <DbContext-Migrations-Configuration-Class-withNamespaces> <Migrations-Name>

  • Cập nhật-Cơ sở dữ liệu -cấu hình <DbContext-Migrations-Configuration-Class-withNamespaces> -Verbose

Hãy kích hoạt tính năng di chuyển cho MyStudentContext bằng cách thực hiện lệnh sau trong Bảng điều khiển Trình quản lý Gói.

PM→ enable-migrations -ContextTypeName:EFCodeFirstDemo.MyStudentContext

Khi nó được thực thi, chúng tôi sẽ thêm mô hình vào lịch sử di chuyển và để làm điều đó, chúng tôi phải kích hoạt lệnh bổ sung di chuyển trong cùng một bảng điều khiển.

PM→ add-migration -configuration EFCodeFirstDemo.Migrations.Configuration Initial

Bây giờ chúng ta hãy thêm một số dữ liệu vào bảng Học sinh và Giáo viên trong cơ sở dữ liệu.

static void Main(string[] args) {

   using (var context = new MyStudentContext()) {
	
      //// Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", 
         LastName = "Bomer", 
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         //Age = 24
      };

      context.Students.Add(student);

      var student1 = new Student {
         FirstMidName = "Mark",
         LastName = "Upston", 
         EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
         //Age = 30
      };

      context.Students.Add(student1);
      context.SaveChanges();
      // Display all Students from the database
      var students = (from s in context.Students orderby s.FirstMidName
         select s).ToList<Student>();
		
      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }

   using (var context = new MyTeacherContext()) {

      //// Create and save a new Teachers
      Console.WriteLine("Adding new teachers");

      var student = new Teacher {
         FirstMidName = "Alain", 
         LastName = "Bomer", 
         HireDate = DateTime.Parse(DateTime.Today.ToString())
         //Age = 24
      };

      context.Teachers.Add(student);

      var student1 = new Teacher {
         FirstMidName = "Mark", 
         LastName = "Upston", 
         HireDate = DateTime.Parse(DateTime.Today.ToString())
         //Age = 30
      };

      context.Teachers.Add(student1);
      context.SaveChanges();
  
      // Display all Teachers from the database
      var teachers = (from t in context.Teachers orderby t.FirstMidName
         select t).ToList<Teacher>();
		
      Console.WriteLine("Retrieve all teachers from the database:");

      foreach (var teacher in teachers) {
         string name = teacher.FirstMidName + " " + teacher.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", teacher.ID, name);
      }

      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Khi đoạn mã trên được thực thi, bạn sẽ thấy hai bảng khác nhau được tạo cho hai mô hình khác nhau như thể hiện trong hình sau.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.

Trước Entity Framework 6, Entity Framework không nhận ra các thực thể hoặc kiểu phức tạp được lồng trong các thực thể hoặc kiểu phức tạp khác. Khi Entity Framework tạo mô hình, các kiểu lồng nhau chỉ biến mất.

Hãy xem một ví dụ đơn giản, trong đó chúng ta có mô hình cơ bản với ba thực thể Sinh viên, Khóa học và Ghi danh.

  • Hãy thêm thuộc tính Identity, là kiểu Person. Person là một thực thể khác, chứa các thuộc tính Ngày sinh và Tên cha.

  • Theo thuật ngữ của Entity Framework, vì nó không có danh tính và là một phần của một thực thể, nó là một loại phức hợp Entity Framework và chúng tôi thực sự đã hỗ trợ cho các loại phức tạp kể từ phiên bản đầu tiên của Entity Framework.

  • Loại Người không được lồng vào nhau như được hiển thị trong đoạn mã sau.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
   public Person Identity { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Person {

   public Person(string fatherName, DateTime birthDate) {
      FatherName = fatherName;
      BirthDate = birthDate;
   }
	
   public string FatherName { get; set; }
   public DateTime BirthDate { get; set; }
}

Entity Framework sẽ biết cách duy trì các loại Person khi nó cũng được sử dụng trong các phiên bản trước.

Bằng cách sử dụng Entity Framework Power Tool, chúng ta sẽ thấy cách Entity Framework diễn giải mô hình. Nhấp chuột phải vào tệp Program.cs và chọn Khung thực thể → Xem Mô hình Dữ liệu Thực thể (Chỉ đọc)

Bây giờ bạn sẽ thấy thuộc tính Identity được định nghĩa trong lớp Sinh viên.

Nếu lớp Person này không được sử dụng bởi bất kỳ thực thể nào khác, thì chúng ta có thể lồng nó vào bên trong lớp Student, nhưng phiên bản Entity Framework trước đó không thừa nhận các kiểu lồng nhau.

Trong phiên bản cũ hơn, bạn tạo lại mô hình, không chỉ kiểu không được nhận dạng, mà bởi vì nó không có ở đó, thuộc tính cũng không ở đó, vì vậy Entity Framework sẽ không giữ lại kiểu Person.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
	
   public DateTime EnrollmentDate { get; set; }
   public Person Identity { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

   public class Person {

      public Person(string fatherName, DateTime birthDate) {
         FatherName = fatherName;
         BirthDate = birthDate;
      }

      public string FatherName { get; set; }
      public DateTime BirthDate { get; set; }
   }
}

Với Entity Framework 6, các thực thể lồng nhau và các kiểu phức tạp được nhận dạng. Trong đoạn mã trên, bạn có thể thấy Người được lồng trong lớp Sinh viên.

Khi bạn sử dụng Công cụ Power của Entity Framework để hiển thị cách Entity Framework diễn giải mô hình lần này, có thuộc tính Identity thực và kiểu phức hợp Person. Vì vậy, Entity Framework sẽ duy trì dữ liệu đó.

Bây giờ bạn có thể thấy rằng Identity là một loại thực thể lồng nhau, không được hỗ trợ trước Entity Framework 6.

Chúng tôi khuyên bạn nên thực hiện ví dụ trên theo cách từng bước để hiểu rõ hơn.