跳至页脚内容
.NET 帮助

CQRS模式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 Pattern C# (开发人员的工作原理):图 1 - 创建一个新的 ASP.NET 项目

  3. 为您的项目命名并设置其位置。

  4. 选择 ASP.NET Core 的“Web 应用程序(模型-视图-控制器)”模板。确保您正在定位符合您要求的 .NET Core 版本。 点击创建。

步骤 2 接下来,您需要为 CQRS 组织您的项目。 点击创建。

您可以通过添加文件夹来分隔命令、查询和它们将使用的公共接口来完成这项工作。

在解决方案资源管理器中,右键单击您的项目,转到“添加”,然后选择“新建文件夹”。 创建三个文件夹:“Commands”、“Queries”和“Interfaces”。 在“Interfaces”文件夹中,为您的命令和查询添加接口。 对于命令,您可能会有一个接口 ICommandHandler,其中包含一个 Handle 方法,该方法接受命令并执行操作。

对于查询,您可能会有一个接口 IQueryHandler,其中包含一个 Handle 方法,该方法接收查询并返回数据。 CQRS Pattern C# (开发人员的工作原理):图 2 - 文件组织示例 ### 步骤 3

现在,让我们添加一个命令和查询来演示。

假设您的应用程序管理任务,您想添加一项任务(命令)并检索任务(查询)。

在“Interfaces”文件夹中,添加两个接口: 在“Commands”文件夹中,添加一个类 AddItemCommand,包含任务详细信息的属性。

同时,添加一个类 AddItemCommandHandler,它实现了 ICommandHandler 并包含将任务添加到数据库的逻辑。

// 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

在“Queries”文件夹中,添加一个类 GetTasksQuery,表示对任务的请求。 再添加一个类 GetTasksQueryHandler,它实现了 IQueryHandler 并包含从数据库检索任务的逻辑。

作为一个简单的例子,您的 AddItemCommand 可能如下所示: 还有 AddItemCommandHandler

您的 GetItemsQuery 如果不需要任何参数来获取任务,它可以是空的,而 GetItemsQueryHandler 可能如下所示:

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

在您的 ASP.NET 控制器中,您将使用这些处理器处理命令和查询。

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

对于添加任务,控制器操作将创建一个 AddTaskCommand,从表单数据中设置其属性,然后传递给一个 AddTaskCommandHandler 实例来处理。

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

对于检索任务,它将调用一个 GetTasksQueryHandler 来获取数据并将其传递到视图。 ### 在控制器中连接它 设置好命令和查询后,您现在可以在控制器中使用它们。

以下是如何在ItemsController类中做到这一点:

要连接这一切,特别是在 ASP.NET Core 中使用依赖注入(DI)时,您需要在Startup.cs文件中向 DI 容器注册您的命令和查询处理器。这样,ASP.NET 在需要时可以提供您的处理器实例。 这里是一个注册处理器的非常基本的例子:

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

在 CQRS 的实际应用中,写操作数据模型和读操作数据模型之间的区别是基础性的,这确保了架构支持多种优化方法来处理数据。

IronPDF: C# PDF 库

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 Pattern C# (开发人员的工作原理):图 3 - IronPDF 网页

探索 IronPDF 用于 PDF 管理 是一个为使用 C# 编程语言的开发人员提供的工具,允许他们在其应用程序内部直接创建、读取和编辑 PDF 文档。

该库用户友好,使整合 PDF 功能(如生成 PDF 报告、发票或从 HTML 创建 PDF代码)更加简单。

IronPDF 支持多种功能,包括编辑 PDF 中的文本和图像、建立文档安全性和将网页转换为 PDF 格式。 其多功能性和易用性使其成为希望在项目中实现 PDF 操作的开发人员的宝贵资源。 IronPDF 以其HTML 到 PDF 的转换能力而著称,保持所有的布局和样式不变。 它从网页内容创建 PDF,适合报告、发票和文档。

HTML 文件、URL 和 HTML 字符串可以无缝地转换为 PDF。 现在,让我们探讨如何在遵循命令查询职责分离(CQRS)模式的 C# 应用程序中利用 IronPDF。 下面是一个简化示例,演示了如何在 CQRS 设置中使用 IronPDF 来生成 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

代码示例

这个示例是概念性的,重点是作为命令生成 PDF 文档。 在此示例中,GeneratePdfReportCommand 表示 CQRS 模式中的命令。 它包括一个方法 GenerateReportAsync,接收 reportContent 作为 HTML 字符串和一个 outputPath,用于保存 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

IronPDF 的 HtmlToPdf 类被用于将 HTML 内容转换为 PDF 格式,然后保存到指定的路径。 此设置说明了如何将 PDF 生成功能整合到您的应用程序架构中,尤其是在要求清晰分离关注点的场景中,如 CQRS 所倡导的那样。 CQRS Pattern C# (开发人员的工作原理):图 4 - 输出 PDF CQRS Pattern C# (开发人员的工作原理):图 5 - IronPDF 许可证信息

总结来说,命令查询职责分离(CQRS)模式为在应用程序中分离读写数据的责任提供了结构化的方法。

结论

这种分离不仅使架构更清晰,还提高了系统的灵活性、可扩展性和性能。

通过遵循上述步骤,您可以在 ASP.NET Core 应用程序中实现 CQRS,使用像 MediatR 这样的工具来简化命令、查询及其处理器之间的通信。 将 IronPDF 集成到您的基于 CQRS 的应用程序中,进一步拓宽了其功能,使您能够轻松创建、操作和存储 PDF 文档。 无论您是在生成报告、发票还是任何形式的文档,IronPDF 的全面功能和简单语法都使其成为开发工具箱中的强大工具。

IronPDF 提供了一个免费试用,让您有机会在投入之前探索其功能。 若要持续使用,许可证从$799起价,为您的项目需求提供多种选择。 IronPDF offers a free trial, giving you the chance to explore its capabilities before committing. For continued use, licenses start from $799, providing various options to match your project's needs.

常见问题解答

什么是软件开发中的CQRS模式?

CQRS模式,全称命令查询职责分离,是一种将应用程序中的数据读取与写入分开的设计策略。这种分离允许独立优化命令(写入)和查询(读取)操作,从而增强性能和可扩展性。

CQRS如何提高.NET应用程序的性能?

CQRS通过为读取和写入操作使用不同的数据模型来提高.NET应用程序的性能,从而使开发者能够独立优化各个部分。这提高了处理复杂业务逻辑的可扩展性和效率。

在CQRS设置中使用MediatR有什么优势?

MediatR是一个中介者模式库,通过减少.NET应用程序中组件之间的耦合来促进CQRS。它作为一个中介,管理命令、查询及其处理程序之间的交互。

在C#应用程序中,IronPDF如何补充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 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。