跳至頁尾內容
開發者更新

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

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. 選擇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,其中包含任務詳細資訊的屬性。 還要新增一個類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
$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

程式碼範例

現在,讓我們探索IronPDF如何在遵循指令查詢責任分離(CQRS)模式的C#應用中使用。 下面是一個簡化的範例,展示在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,接受reportContent作為一個HTML字串和一個outputPath,表示PDF報告將被保存的位置。 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提供了免費試用,讓您在決定是否承諾之前探索其功能。 要繼續使用,授權始於$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核心程式碼庫的原開發者,他從創立以來就一直在塑造公司的產品架構,與首席執行官Cameron Rimington一起將公司轉變為服務於NASA、特斯拉和全球政府公司的50多名人員的公司。

Jacob擁有曼徹斯特大學的土木工程一等榮譽學士學位(BEng),於1998-2001年之間獲得。在1999年於倫敦創辦他的第一家軟體公司並於2005年建立了他的第一批.NET元組件後,他專注於解決Microsoft生態系統中的複雜問題。

他的旗艦IronPDF和Iron Suite .NET程式庫在全球獲得了超過3000萬次NuGet安裝依據,他的基礎程式碼基繼續支援著世界各地開發者使用的工具。擁有25年的商業經驗和41年的程式設計專業知識,他仍專注於推動企業級C#、Java和Python PDF技術的創新,同時指導下一代技術領導者。

Iron 支援團隊

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