跳過到頁腳內容
.NET幫助

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

CQRS 簡介

CQRS 代表命令查詢責任區隔。 這是一種專注於將資料的讀取與寫入分離的模式。 基於以下幾個原因,這個區別非常重要。 首先,它可以更靈活地優化各項作業,提高應用程式的效能和擴充性。 當您分開指令 (寫入) 和查詢 (讀取) 時,您可以獨立優化它們。

例如,複雜的應用程式可能需要快速的讀取作業,但可以容忍較慢的寫入作業。 透過應用 CQRS,開發人員可以使用不同的資料模型來進行讀取和寫入,分隔資料存取層,以因應各項作業的特定需求。 在本文中,我們將探討 CQRS 模式和適用於 .NET 開發人員的 *IronPDF 函式庫的概念。

核心概念與元件

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 應用程式"專案類型。 按一下下一步。

![CQRS Pattern C# (How It Works For Developers):圖 1 - 建立新的 ASP.NET 專案](/static-assets/pdf/blog/cqrs-pattern-csharp/cqrs-pattern-csharp-1.webp)。

3.為專案命名並設定其位置。 按一下建立。 4.選擇 ASP.NET Core 的"Web 應用程式 (模型-視圖-控制器)"範本。 確保您的目標是適合您需求的 .NET Core 版本。 按一下建立。

步驟 2

接下來,您要為 CQRS 組織專案。 您可以透過新增資料夾,將命令、查詢和他們會用到的常用介面分隔開來。 在"解決方案總管"中,在專案上按一下滑鼠右鍵,移至"新增",再移至"新增資料夾"。 建立三個資料夾:"命令"、"查詢 "和 "介面"。

在"介面"資料夾中,為您的指令和查詢新增介面。 對於命令,您可能有一個介面 ICommandHandler,其中有一個方法 Handle,可以接收命令並執行動作。 對於查詢,您可以有一個介面 IQueryHandler,其中有一個方法 Handle,可以接收查詢並傳回資料。

CQRS Pattern C# (How It Works For Developers):圖 2 - 檔案如何組織的範例

步驟 3

現在,讓我們加入一個指令和查詢來進行示範。 假設您的應用程式管理任務,而您想要新增任務 (指令) 及擷取任務 (查詢)。

在"介面"資料夾中,新增兩個介面:

// 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 Pattern C# (How It Works For Developers):圖 3 - IronPDF 網頁

Explore IronPDF for PDF management 是專為使用 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 Pattern C# (How It Works For Developers):圖 4 - 輸出的 PDF

結論

CQRS Pattern C# (How It Works For Developers):圖 5 - IronPDF 授權資訊

總而言之,命令查詢責任區隔 (CQRS) 模式提供了一種結構化的方法,可將應用程式中讀取和寫入資料的責任分開。 這種分離不僅能釐清架構,還能增強系統的彈性、可擴充性和效能。 按照上述步驟,您可以在 ASP.NET Core 應用程式中實作 CQRS,使用 MediatR 等工具簡化指令、查詢及其處理程式之間的溝通。

將 IronPDF 整合到您基於 CQRS 的應用程式中,可進一步擴展其功能,讓您毫不費力地建立、處理和儲存 PDF 文件。 無論您是要產生報表、發票或任何形式的文件,IronPDF 齊全的功能和簡單直接的語法都能讓它成為您開發工具包中的強大工具。 IronPDF 提供 免費試用,讓您有機會在承諾之前探索其功能。 如需繼續使用,許可證從 $999 開始,提供各種選項以滿足您專案的需求。

常見問題解答

什麼是軟體開發中的 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 提供免費的試用版,讓開發者在購買決策前探索其功能和特性。

Jacob Mellor, Team Iron 首席技術官
首席技術官

Jacob Mellor是Iron Software的首席技術官,也是開創C# PDF技術的前瞻性工程師。作為Iron Software核心代碼庫的原始開發者,他自公司成立以來就塑造了公司的產品架構,並與CEO Cameron Rimington將公司轉型為服務NASA、Tesla以及全球政府機構的50多人公司。

Jacob擁有曼徹斯特大學土木工程一級榮譽學士學位(1998年–2001年)。他於1999年在倫敦開立首家軟體公司,並於2005年建立了他的第一個.NET組件,專注於解決Microsoft生態系統中的複雜問題。

他的旗艦作品IronPDF和Iron Suite .NET程式庫全球已獲得超過3000萬次NuGet安裝,他的基礎代碼不斷在全球各地驅動開發者工具。擁有25年以上的商業經驗和41年的編碼專業知識,Jacob仍然專注於推動企業級C#、Java和Python PDF技術的創新,同時指導下一代技術領導者。

鋼鐵支援團隊

我們每週 5 天,每天 24 小時在線上。
聊天
電子郵件
打電話給我