Trong cơ sở dữ liệu quan hệ, một bảng có thể được kết nối với bảng khác bằng cách sử dụng ràng buộc Khóa chính-Khóa ngoại. Để lấy các bản ghi liên quan, chúng ta áp dụng lệnh JOIN SQL. Trong Dapper, chúng ta có thể sử dụng phương thức Query thực hiện truy vấn SQL có chứa lệnh JOIN để lấy các bản ghi liên quan.
Hãy hiểu chi tiết cách làm việc với quan hệ 1-1 trong Dapper.
Quan hệ 1-1 trong Dapper
Trong quan hệ 1-1, bản ghi của bảng đầu tiên sẽ được liên kết với không hoặc bản ghi của bảng thứ hai. Chúng ta có 2 bảng có quan hệ 1-1, đó là:
- Invoice (Hóa đơn)
- InvoiceDetail (Chi tiết hóa đơn)
Bản ghi Invoice có thể có không hoặc một bản ghi InvoiceDetail liên quan. Tương tự, mỗi bản ghi InvoiceDetail có thể có chính xác bản ghi Invoice.
CREATE TABLE [dbo].[Invoice] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[CustomerName] VARCHAR (50) NOT NULL,
[ItemName] VARCHAR (50) NOT NULL,
[Price] MONEY NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[InvoiceDetail] (
[InvoiceId] INT NOT NULL,
[BillingAddress] VARCHAR (250) NOT NULL,
[PaymentMethod] VARCHAR (50) NOT NULL,
[SalesRepresentative] VARCHAR (250) NOT NULL,
[AddedOn] DATE DEFAULT (getdate()) NOT NULL,
PRIMARY KEY CLUSTERED ([InvoiceId] ASC),
CONSTRAINT [FK_InvoiceDetail_Invoice] FOREIGN KEY ([InvoiceId]) REFERENCES [dbo].[Invoice] ([Id])
);
Quan hệ 1-1 giữa 2 bảng được hiển thị như sau:
Để lấy các bản ghi từ cả hai bảng này, thực hiện truy vấn SQL Inner JOIN với phương thức Query của Dapper và sau đó kết quả được ánh xạ vào 2 danh sách được định dạng mạnh - Invoice.cs và InvoiceDetail.cs.
Đầu tiên, xác định 2 class - Invoice.cs và InvoiceDetail.cs.
public class Invoice
{
public int Id { get; set; }
public string CustomerName { get; set; }
public string ItemName { get; set; }
public int Price { get; set; }
public InvoiceDetail InvoiceDetail { get; set; }
}
public class InvoiceDetail
{
public int InvoiceId { get; set; }
public string BillingAddress { get; set; }
public string PaymentMethod { get; set; }
public string SalesRepresentative { get; set; }
public DateTime AddedOn { get; set; }
}
Lưu ý Invoice.cs có thuộc tính kiểu InvoiceDetail. Chúng ta sẽ thấy sau này làm cách nào để điền giá trị cho thuộc tính này từ bảng InvoiceDetail.
Phương thức Query của Dapper cho quan hệ 1-1
Tiếp theo, sử dụng phương thức Query để lấy các bản ghi từ cả hai bảng. Mã đầy đủ được đưa ra dưới đây:
//connection string
string myCS = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=DapperStart;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
string sql = "SELECT * FROM Invoice AS A INNER JOIN InvoiceDetail AS B ON A.Id = B.InvoiceId;";
using (var connection = new SqlConnection(myCS))
{
var invoices = connection.Query<Invoice, InvoiceDetail, Invoice>(
sql,
(invoice, invoiceDetail) =>
{
invoice.InvoiceDetail = invoiceDetail;
return invoice;
},
splitOn: "InvoiceId")
.Distinct()
.ToList();
}
Giải thích: Chúng ta sử dụng truy vấn sql inner join được thực thi bởi phương thức Query. Phương thức truy vấn đã được định nghĩa là Query, cho biết nó sẽ lấy các tham số "Invoice và InvoiceDetail" và sau đó trả về kết quả kiểu "Invoice".
Xem mã hàm delegate Func xử lý kết quả truy vấn Inner Join.
(invoice, invoiceDetail) =>
{
invoice.InvoiceDetail = invoiceDetail;
return invoice;
}
Mã hàm delegate Func điền giá trị cho thuộc tính InvoiceDetail của lớp Invoice.cs mà truy vấn Inner Join đã trả về. Nó điền bản ghi liên quan từ bảng InvoiceDetail.
Tiếp theo, "InvoiceId" cho biết Dapper phải chia kết quả truy vấn inner join trên cột "InvoiceId". Mọi thứ cho đến cột đó sẽ được ánh xạ vào kiểu "Invoice" đầu tiên, và mọi thứ từ cột đó trở đi sẽ được ánh xạ vào kiểu "InvoiceDetail" thứ hai.
Chúng ta áp dụng breakpoint để kiểm tra mã. Bạn có thể thấy nó điền thuộc tính "InvoiceDetail" của Invoice.cs với các bản ghi liên quan. Xem hình bên dưới.