跳過到頁腳內容
.NET幫助

CQRS Pattern C#(對於開發者的運行原理)

介紹 CQRS

CQRS 代表命令查詢責任分離。 這種模式專注於將數據的讀取和寫入分開。 這種區分對於多個理由至關重要。 首先,它允許對每個操作進行更靈活的優化,從而改善應用程式的性能和可擴展性。 當您分開命令(寫入)和查詢(讀取)時,您可以獨立優化它們。

例如,復雜的應用程式可能需要快速的讀取操作,但可以容忍較慢的寫入操作。 通過應用 CQRS,開發人員可以對讀寫使用不同的數據模型,分隔數據訪問層以滿足每個操作的具體需求。 在本文中,我們將探索 CQRS 模式的概念和 *IronPDF for .NET 開發者的庫。

核心概念和組件

CQRS 的核心在於分離命令和查詢操作,每個操作處理數據交互的不同方面。 理解這些組件對於有效執行這個模式至關重要。

  • 命令:這些負責更新數據。 命令體現了複雜的商業邏輯,可以通過行動改變數據存儲總的數據狀態,而不返回任何信息。 命令專門負責處理寫入數據的任務,直接影響應用程式的狀態而不產生任何輸出。 例如,添加新用戶或更新現有產品詳情是由命令執行的操作。

  • 查詢:查詢由查詢處理器管理,負責檢索數據或數據傳輸對象而不改變系統的狀態。 它們是您對數據的疑問。 例如,獲取用戶檔案或列出庫存中所有可用產品都是查詢。 查詢返回數據但確保不會修改數據或其狀態。

在 .NET 應用中實現 CQRS 的一個流行工具是 MediatR,一個中介者模式庫。 它有助於減少應用程式各個組件之間的耦合,使它們間接地進行通信。 MediatR 通過在命令/查詢和其處理器之間進行中介,來促進命令和查詢的處理。

使用 ASP.NET Core 的實施

在 ASP.NET Core 中實施 CQRS 模式涉及設置您的專案以分離命令和查詢,使用像 MediatR 這樣的庫來進行中介。 這裡是如何在您的 ASP.NET Core 應用中設置 CQRS 的簡化概述。

步驟 1:設置您的 ASP.NET 應用

  1. 打開 Visual Studio,選擇創建一個新項目。
  2. 搜索並選擇 "ASP.NET Core Web Application" 項目類型。 單擊下一步。

CQRS 模式 C#(如何為開發者工作):圖 1 - 創建一個新的 ASP.NET 項目

  1. 給您的項目命名並設置其位置。 點擊創建。
  2. 為 ASP.NET Core 選擇 "Web 應用(模型-視圖-控制器)" 模板。 確保您面向符合您需求的 .NET Core 版本。 點擊創建。

步驟 2

接下來,您需要為 CQRS 組織您的項目。 您可以通過添加文件夾來分隔命令、查詢和它們將使用的公共介面。 在“解決方案資源管理器”中,右鍵單擊您的項目,選擇“添加”,然後選擇“新建文件夾”。 創建三個文件夾:“Commands”、“Queries”和“Interfaces”。

在“Interfaces”文件夾中,為您的命令和查詢添加介面。 對於命令,您可能有一個名為 ICommandHandler 的介面,其內含接收命令並執行動作的方法 Handle。 對於查詢,您可以有一個名為 IQueryHandler 的介面,其中有接收查詢並返回數據的方法 Handle

CQRS 模式 C#(如何為開發者工作):圖 2 - 文件組織示例

步驟 3

現在讓我們添加一個命令和查詢以示範。 假設您的應用程序管理任務,而您希望添加一個任務(命令)並檢索任務(查詢)。

在“Interfaces”文件夾中,添加兩個介面:

// Define interfaces for your handlers:
public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public interface IQueryHandler<TQuery, TResult>
{
    TResult Handle(TQuery query);
}
// Define interfaces for your handlers:
public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public interface IQueryHandler<TQuery, TResult>
{
    TResult Handle(TQuery query);
}
' Define interfaces for your handlers:
Public Interface ICommandHandler(Of TCommand)
	Sub Handle(ByVal command As TCommand)
End Interface

Public Interface IQueryHandler(Of TQuery, TResult)
	Function Handle(ByVal query As TQuery) As TResult
End Interface
$vbLabelText   $csharpLabel

在“Commands”文件夾中,添加一個名為 AddItemCommand 的類,其中包含任務詳情的屬性。 還要添加一個實施 ICommandHandler 的類 AddItemCommandHandler,其中包含將任務添加到數據庫的邏輯。

在“Queries”文件夾中,添加一個類 GetTasksQuery,它代表對任務的請求。 添加另一個實現 IQueryHandler 的類 GetTasksQueryHandler,其中包含從數據庫檢索任務的邏輯。

對於一個簡單的示例,您的 AddItemCommand 可能看起來像這樣:

public class AddItemCommand
{
    public string Name { get; set; }
    public int Quantity { get; set; }

    // Constructor
    public AddItemCommand(string name, int quantity)
    {
        Name = name;
        Quantity = quantity;
    }
}
public class AddItemCommand
{
    public string Name { get; set; }
    public int Quantity { get; set; }

    // Constructor
    public AddItemCommand(string name, int quantity)
    {
        Name = name;
        Quantity = quantity;
    }
}
Public Class AddItemCommand
	Public Property Name() As String
	Public Property Quantity() As Integer

	' Constructor
	Public Sub New(ByVal name As String, ByVal quantity As Integer)
		Me.Name = name
		Me.Quantity = quantity
	End Sub
End Class
$vbLabelText   $csharpLabel

AddItemCommandHandler

public class AddItemCommandHandler : ICommandHandler<AddItemCommand>
{
    public void Handle(AddItemCommand command)
    {
        // Here, you'd add the item to your database, for example, to have employee data stored
        Console.WriteLine($"Adding item: {command.Name} with quantity {command.Quantity}");
        // Add database logic here
    }
}
public class AddItemCommandHandler : ICommandHandler<AddItemCommand>
{
    public void Handle(AddItemCommand command)
    {
        // Here, you'd add the item to your database, for example, to have employee data stored
        Console.WriteLine($"Adding item: {command.Name} with quantity {command.Quantity}");
        // Add database logic here
    }
}
Public Class AddItemCommandHandler
	Implements ICommandHandler(Of AddItemCommand)

	Public Sub Handle(ByVal command As AddItemCommand)
		' Here, you'd add the item to your database, for example, to have employee data stored
		Console.WriteLine($"Adding item: {command.Name} with quantity {command.Quantity}")
		' Add database logic here
	End Sub
End Class
$vbLabelText   $csharpLabel

您的 GetItemsQuery 可以是空的,如果它不需要任何參數來取任務,GetItemsQueryHandler 可能看起來像這樣:

public class GetItemsQuery
{
    // This class might not need any properties, depending on your query
}

namespace CQRS_testing.Queries
{
    using CQRS_testing.Interfaces;

    public class GetItemsQueryHandler : IQueryHandler<GetItemsQuery, IEnumerable<string>>
    {
        public IEnumerable<string> Handle(GetItemsQuery query)
        {
            // Here, you'd fetch items from your database
            return new List<string> { "Item1", "Item2" };
        }
    }
}
public class GetItemsQuery
{
    // This class might not need any properties, depending on your query
}

namespace CQRS_testing.Queries
{
    using CQRS_testing.Interfaces;

    public class GetItemsQueryHandler : IQueryHandler<GetItemsQuery, IEnumerable<string>>
    {
        public IEnumerable<string> Handle(GetItemsQuery query)
        {
            // Here, you'd fetch items from your database
            return new List<string> { "Item1", "Item2" };
        }
    }
}
Imports CQRS_testing.Interfaces

Public Class GetItemsQuery
	' This class might not need any properties, depending on your query
End Class

Namespace CQRS_testing.Queries

	Public Class GetItemsQueryHandler
		Implements IQueryHandler(Of GetItemsQuery, IEnumerable(Of String))

		Public Function Handle(ByVal query As GetItemsQuery) As IEnumerable(Of String)
			' Here, you'd fetch items from your database
			Return New List(Of String) From {"Item1", "Item2"}
		End Function
	End Class
End Namespace
$vbLabelText   $csharpLabel

在您的 ASP.NET 控制器中,您將使用這些處理器來處理命令和查詢。 為了添加一個任務,控制器行動將創建一個 AddTaskCommand,並從表單數據中設置其屬性,然後將其傳遞給一個 AddTaskCommandHandler 實例進行處理。 為了檢索任務,它將調用 GetTasksQueryHandler 來獲取數據並將其傳遞給視圖。

在控制器中接線

有了您的命令和查詢設置,您現在可以在控制器中使用它們。 以下是您可能在 ItemsController 類中採取的方法:

public class ItemsController : Controller
{
    private readonly ICommandHandler<AddItemCommand> _addItemHandler;
    private readonly IQueryHandler<GetItemsQuery, IEnumerable<string>> _getItemsHandler;

    // Constructor injection is correctly utilized here
    public ItemsController(ICommandHandler<AddItemCommand> addItemHandler, IQueryHandler<GetItemsQuery, IEnumerable<string>> getItemsHandler)
    {
        _addItemHandler = addItemHandler;
        _getItemsHandler = getItemsHandler;
    }

    public IActionResult Index()
    {
        // Use the injected _getItemsHandler instead of creating a new instance
        var query = new GetItemsQuery();
        var items = _getItemsHandler.Handle(query);
        return View(items);
    }

    [HttpPost]
    public IActionResult Add(string name, int quantity)
    {
        // Use the injected _addItemHandler instead of creating a new instance
        var command = new AddItemCommand(name, quantity);
        _addItemHandler.Handle(command);
        return RedirectToAction("Index");
    }
}
public class ItemsController : Controller
{
    private readonly ICommandHandler<AddItemCommand> _addItemHandler;
    private readonly IQueryHandler<GetItemsQuery, IEnumerable<string>> _getItemsHandler;

    // Constructor injection is correctly utilized here
    public ItemsController(ICommandHandler<AddItemCommand> addItemHandler, IQueryHandler<GetItemsQuery, IEnumerable<string>> getItemsHandler)
    {
        _addItemHandler = addItemHandler;
        _getItemsHandler = getItemsHandler;
    }

    public IActionResult Index()
    {
        // Use the injected _getItemsHandler instead of creating a new instance
        var query = new GetItemsQuery();
        var items = _getItemsHandler.Handle(query);
        return View(items);
    }

    [HttpPost]
    public IActionResult Add(string name, int quantity)
    {
        // Use the injected _addItemHandler instead of creating a new instance
        var command = new AddItemCommand(name, quantity);
        _addItemHandler.Handle(command);
        return RedirectToAction("Index");
    }
}
Public Class ItemsController
	Inherits Controller

	Private ReadOnly _addItemHandler As ICommandHandler(Of AddItemCommand)
	Private ReadOnly _getItemsHandler As IQueryHandler(Of GetItemsQuery, IEnumerable(Of String))

	' Constructor injection is correctly utilized here
	Public Sub New(ByVal addItemHandler As ICommandHandler(Of AddItemCommand), ByVal getItemsHandler As IQueryHandler(Of GetItemsQuery, IEnumerable(Of String)))
		_addItemHandler = addItemHandler
		_getItemsHandler = getItemsHandler
	End Sub

	Public Function Index() As IActionResult
		' Use the injected _getItemsHandler instead of creating a new instance
		Dim query = New GetItemsQuery()
		Dim items = _getItemsHandler.Handle(query)
		Return View(items)
	End Function

	<HttpPost>
	Public Function Add(ByVal name As String, ByVal quantity As Integer) As IActionResult
		' Use the injected _addItemHandler instead of creating a new instance
		Dim command = New AddItemCommand(name, quantity)
		_addItemHandler.Handle(command)
		Return RedirectToAction("Index")
	End Function
End Class
$vbLabelText   $csharpLabel

將一切接線起來,特別是如果您在 ASP.NET Core 中使用依賴注入 (DI),您需要在 Startup.cs 文件中將您的命令和查詢處理器註冊到 DI 容器中。這樣,ASP.NET 在需要時可以提供您的處理器的實例。

以下是一個註冊處理器的非常基本的例子:

builder.Services.AddTransient<ICommandHandler<AddItemCommand>, AddItemCommandHandler>();
builder.Services.AddTransient<IQueryHandler<GetItemsQuery, IEnumerable<string>>, GetItemsQueryHandler>();
builder.Services.AddTransient<ICommandHandler<AddItemCommand>, AddItemCommandHandler>();
builder.Services.AddTransient<IQueryHandler<GetItemsQuery, IEnumerable<string>>, GetItemsQueryHandler>();
builder.Services.AddTransient(Of ICommandHandler(Of AddItemCommand), AddItemCommandHandler)()
builder.Services.AddTransient(Of IQueryHandler(Of GetItemsQuery, IEnumerable(Of String)), GetItemsQueryHandler)()
$vbLabelText   $csharpLabel

在 CQRS 的實際應用中,寫入操作和讀取操作的數據模型之間的區別是基礎,確保架構支持多種且優化的數據處理方式。

IronPDF:C# PDF 庫

CQRS 模式 C#(如何為開發者工作):圖 3 - IronPDF 網頁

探索 IronPDF 用於 PDF 管理 是一個面向 C# 程式設計語言開發者的工具,使它們能夠直接在應用中創建、讀取和編輯 PDF 文檔。 此庫用戶友好,使其更容易集成 PDF 功能,比如生成 PDF 報告、發票或從 HTML 創建 PDF代碼。 IronPDF 支持多種功能,包括編輯 PDF 中的文本和圖像、設置文檔安全性以及將網頁轉換為 PDF 格式。 其多功能性和易用性使其成為開發者希望在項目中實施 PDF 操作的寶貴資源。

IronPDF 以其HTML 到 PDF 轉換功能而出色,保持所有佈局和樣式完整。 它可以從網頁內容創建 PDF,適用於報告、發票和文檔。 HTML 文件、URL 和 HTML 字符串都可以無縫轉換為 PDF。

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

代碼示例

現在,讓我們探索如何在遵循命令查詢責任分離(CQRS)模式的 C# 應用中使用 IronPDF。 以下是一個簡化的示例,展示了如何在 CQRS 設置中使用 IronPDF 生成 PDF 報告。 這個例子是概念上的,重點在於作為命令生成 PDF 文檔。

using IronPdf;
using System.Threading.Tasks;

namespace PdfGenerationApp.Commands
{
    public class GeneratePdfReportCommand
    {
        // Command handler that generates a PDF report
        public async Task GenerateReportAsync(string reportContent, string outputPath)
        {
            // Initialize the IronPDF HTML to PDF renderer
            var renderer = new ChromePdfRenderer();
            // Use IronPDF to generate a PDF from HTML content
            var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(reportContent));
            // Save the generated PDF to a specified path
            pdf.SaveAs(outputPath);
        }
    }
}
using IronPdf;
using System.Threading.Tasks;

namespace PdfGenerationApp.Commands
{
    public class GeneratePdfReportCommand
    {
        // Command handler that generates a PDF report
        public async Task GenerateReportAsync(string reportContent, string outputPath)
        {
            // Initialize the IronPDF HTML to PDF renderer
            var renderer = new ChromePdfRenderer();
            // Use IronPDF to generate a PDF from HTML content
            var pdf = await Task.Run(() => renderer.RenderHtmlAsPdf(reportContent));
            // Save the generated PDF to a specified path
            pdf.SaveAs(outputPath);
        }
    }
}
Imports IronPdf
Imports System.Threading.Tasks

Namespace PdfGenerationApp.Commands
	Public Class GeneratePdfReportCommand
		' Command handler that generates a PDF report
		Public Async Function GenerateReportAsync(ByVal reportContent As String, ByVal outputPath As String) As Task
			' Initialize the IronPDF HTML to PDF renderer
			Dim renderer = New ChromePdfRenderer()
			' Use IronPDF to generate a PDF from HTML content
			Dim pdf = Await Task.Run(Function() renderer.RenderHtmlAsPdf(reportContent))
			' Save the generated PDF to a specified path
			pdf.SaveAs(outputPath)
		End Function
	End Class
End Namespace
$vbLabelText   $csharpLabel

在這個例子中,GeneratePdfReportCommand 代表 CQRS 模式中的一個命令。 它包含一個 GenerateReportAsync 方法,該方法接收作為 HTML 字符串的 reportContent 和保存 PDF 報告的 outputPath。 IronPDF 的 HtmlToPdf 類用於將 HTML 內容轉換為 PDF 格式,隨後保存到指定路徑。 此設置說明了如何將 PDF 生成功能集成到您的應用程序架構中,特別是在需要明確的關注點分離的情況下,如 CQRS 所推廣的。

CQRS 模式 C#(如何為開發者工作):圖 4 - 輸出的 PDF

結論

CQRS 模式 C#(如何為開發者工作):圖 5 - IronPDF 許可證信息

總結,命令查詢責任分離(CQRS)模式提供了一種將數據讀寫責任分開的結構化方法。 這種分離不僅明確了架構,還提升了系統的靈活性、可擴展性和性能。 通過遵循上述步驟,您可以在 ASP.NET Core 應用中實施 CQRS,使用像 MediatR 這樣的工具來簡化命令、查詢及其處理程序之間的通信。

將 IronPDF 集成到您的基於 CQRS 的應用中進一步擴大其能力,使您能夠輕鬆創建、操作和存儲 PDF 文檔。 無論您是生成報告、發票或任何形式的文檔,IronPDF 的全面功能和簡潔語法都使其成為開發工具箱中的強大工具。 IronPDF 提供免費試用,讓您在承諾前探索其能力。 續約使用的許可證從 $799 起提供,提供多種選項以符合您的項目需求。

常見問題解答

什麼是軟體開發中的 CQRS 模式?

CQRS 模式,即指令查詢責任分離,是一種將應用程式中的資料讀取與寫入分離的設計策略。這種分離允許指令(寫入)和查詢(讀取)操作的獨立優化,提高了效能和擴展性。

CQRS 如何提高 .NET 應用程式的效能?

CQRS 通過為讀寫操作使用不同的資料模型來提高 .NET 應用程式的效能,使開發者可以獨立優化每個部分。這提高了處理複雜業務邏輯的可擴展性和效率。

在 CQRS 設置中使用 MediatR 有什麼優勢?

MediatR 是一個中介者模式庫,可以通過減少 .NET 應用程式中元件之間的耦合來促進 CQRS。它作為一個中介,管理指令、查詢及其處理器之間的互動。

IronPDF 如何在 C# 應用程式中補充 CQRS 模式?

IronPDF 通過提供強大的 PDF 操作功能來補充 CQRS 模式。它允許開發者在應用程式中生成、讀取和編輯 PDF 檔案,非常適合在 CQRS 設置中作為指令操作的一部分來創建 PDF 報告。

為何在 ASP.NET Core 專案中分離指令和查詢是有益的?

在 ASP.NET Core 專案中分離指令和查詢可以增強組織性和清晰度。它允許開發者獨立管理每個方面,提高了可維護性並符合 CQRS 模式的原則。

依賴注入在 CQRS 架構中扮演什麼角色?

依賴注入在 CQRS 架構中至關重要,因為它允許無縫註冊和提供指令和查詢處理器。這確保 ASP.NET Core 應用程式能有效地解析依賴關係並根據需要管理處理器的實例。

如何在 C# 中使用庫將 HTML 轉換為 PDF?

您可以使用 IronPDF 的 RenderHtmlAsPdf 方法將 HTML 字串轉換為 PDF。它還支援使用 RenderHtmlFileAsPdf 將 HTML 文件轉換為 PDF,這對於生成報告和文件非常有用。

我可以在購買前評估 C# 的 PDF 库嗎?

是的,IronPDF 提供免費的試用版,讓開發者在購買決策前探索其功能和特性。

Curtis Chau
技術作家

Curtis Chau 擁有卡爾頓大學計算機科學學士學位,專注於前端開發,擅長於 Node.js、TypeScript、JavaScript 和 React。Curtis 熱衷於創建直觀且美觀的用戶界面,喜歡使用現代框架並打造結構良好、視覺吸引人的手冊。

除了開發之外,Curtis 對物聯網 (IoT) 有著濃厚的興趣,探索將硬體和軟體結合的創新方式。在閒暇時間,他喜愛遊戲並構建 Discord 機器人,結合科技與創意的樂趣。