.NET 幫助

CQRS 模式 C#(它如何對開發者起作用)

發佈 2024年4月3日
分享:

CQRS簡介

CQRS代表指令查詢責任分離。 這是一個專注於將讀取數據與寫入數據分離的模式。 這一區分出於多個原因而顯得尤為重要。 首先,它允許對每個操作進行更靈活的優化,提高應用程式的性能和可擴展性。 當你分開命令時(寫入)和查詢(讀取),您可以獨立優化它們。

例如,一個複雜的應用程式可能需要快速的讀取操作,但可以容忍較慢的寫入操作。 通過應用 CQRS,開發者可以對讀取和寫入使用不同的數據模型,將數據訪問層分隔開來,以滿足每個操作的具體需求。 在本文中,我們將探討 CQRS 模式的概念和IronPDF 函式庫適用於 .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 應用程式」專案類型。 點擊下一步。

    CQRS 模式 C#(它如何為開發人員工作):圖1 - 創建一個新的 ASP.NET 專案

  3. 為您的專案命名並設置其位置。 點擊建立。

  4. 選擇「Web 應用程式」(模型-視圖-控制器)ASP.NET Core 的範本。 確保您選擇適合您需求的 .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
VB   C#

在「Commands」資料夾中,新增一個類別 AddItemCommand,並為其添加任務細節的屬性。 另外,添加一个類別 AddItemCommandHandler,實現 ICommandHandler 並包含將任務添加到數據庫的邏輯。

在 "Queries" 資料夾中,新增一個類別 GetTasksQuery,用來表示對任務的請求。 新增另一個類別 GetTasksQueryHandler,實作 IQueryHandler,並包含從資料庫檢索任務的邏輯。

以下是一個簡單的例子,您的 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
VB   C#

以及 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
VB   C#

您的 GetItemsQuery 如果不需要任何參數來獲取任務,可能會是空的,而 GetItemsQueryHandler 可能是這樣的:

public class GetItemsQuery
{
    // This class might not need any properties, depending on your query
}
using CQRS_testing.Interfaces;
namespace CQRS_testing.Queries
{
    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
}
using CQRS_testing.Interfaces;
namespace CQRS_testing.Queries
{
    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
VB   C#

在您的 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
VB   C#

若要將所有內容連接在一起,特別是如果您使用依賴注入(數字輸入)在 ASP.NET Core 中,您需要在 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)()
VB   C#

在實際應用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
VB   C#

範例程式碼

現在,讓我們探索如何在遵循命令查詢責任分離的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
VB   C#

在此範例中,GeneratePdfReportCommand 代表 CQRS 模式中的一個命令。 它包含一個方法 GenerateReportAsync,該方法接收 reportContent 作為 HTML 字串,以及儲存 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,讓您在承諾之前有機會探索其功能。 要繼續使用,授權起價為 $399,提供多種選項以符合您的項目需求。

< 上一頁
在 C# 中(對開發人員的運作方式)
下一個 >
C# 單元測試(開發者如何運作)

準備開始了嗎? 版本: 2024.12 剛剛發布

免費 NuGet 下載 總下載次數: 11,622,374 查看許可證 >