
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應用程式
-
開啟Visual Studio並選擇建立新專案。
-
搜索並選擇"ASP.NET Core Web應用"專案型別。 單擊下一步。

-
給您的專案命名並設置其位置。 按下建立。
-
選擇ASP.NET Core的"Web應用(模型-視圖-控制器)"模板。 確保您針對適合您需求的.NET Core版本。 按下建立。
步驟2
接下來,您將要組織您的專案以適應CQRS。 您可以通過新增文件夾來分開指令、查詢,以及它們將使用的公共接口來做到這一點。 在解決方案資源管理器中,右鍵點擊您的專案,選擇"新增",然後"新建文件夾"。 建立三個文件夾:"Commands"、"Queries"和"Interfaces"。
在"Interfaces"文件夾中,為您的指令和查詢新增接口。 對於一個指令,您可能會有一個接口ICommandHandler,其中包含一個方法Handle,用於接收指令並執行操作。 對於一個查詢,您可能會有一個接口IQueryHandler,其中包含一個方法Handle,用於接收查詢並返回資料。

步驟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(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在"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 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以及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
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您的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" };
}
}
}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在您的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
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要將所有內容連接起來,特別是如果您在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(Of ICommandHandler(Of AddItemCommand), AddItemCommandHandler)()
builder.Services.AddTransient(Of IQueryHandler(Of GetItemsQuery, IEnumerable(Of String)), GetItemsQueryHandler)()在CQRS的實際應用中,寫操作的資料模型和讀操作的資料模型之間的區分是基礎,確保架構支持多樣化和優化的方法來處理資料。
IronPDF:C# PDF程式庫

探索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");
}
}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程式碼範例
現在,讓我們探索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);
}
}
}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在這個範例中,GeneratePdfReportCommand代表CQRS模式中的一個指令。 它包含一個方法GenerateReportAsync,接受reportContent作為一個HTML字串和一個outputPath,表示PDF報告將被保存的位置。 IronPDF的HtmlToPdf類用於將HTML內容轉換為PDF格式,然後保存到指定路徑。 此設置展示了如何將PDF生成功能整合到應用程式架構中,尤其是在需要明確責任分離的場景中,如CQRS所推廣的情況。

結論
總結一下,指令查詢責任分離(CQRS)模式提供了一種結構化的方法,將讀寫資料的責任分開在您的應用中。 這種分離不僅明確了架構,而且增強了系統的靈活性、可擴展性和性能。 通過遵循上述步驟,您可以在ASP.NET Core中實施CQRS,使用像MediatR這樣的工具來簡化指令、查詢及其處理程式之間的通信。
將IronPDF整合到您的基於CQRS的應用中進一步擴展其功能,使您可以輕鬆建立、操作和儲存PDF文件。 無論您是生成報告、發票或任何形式的文件,IronPDF全面的功能和簡單的語法使其成為您的開發工具包中的強大工具。 IronPDF提供了免費試用,讓您在決定是否承諾之前探索其功能。 要繼續使用,授權始於$999,提供多種選擇以匹配您專案的需求。

Jacob Mellor是Iron Software的首席技術官,一位在C# PDF技術上開創先河的遠見工程師。作為Iron Software核心程式碼庫的原開發者,他從創立以來就一直在塑造公司的產品架構,與首席執行官Cameron Rimington一起將公司轉變為服務於NASA、特斯拉和全球政府公司的50多名人員的公司。
相關文章


