跳至页脚内容
.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 项目

CQRS Pattern C#(开发者的工作原理):图 1 - 创建一个新的 ASP.NET 项目

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

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

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

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

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

CQRS Pattern C#(开发者的工作原理):图 2 - 文件组织示例

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

在"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);
}
$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;
    }
}
$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
    }
}
$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" };
        }
    }
}
$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");
    }
}
$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>();
$vbLabelText   $csharpLabel

在 CQRS 的实际应用中,写操作数据模型与读操作数据模型之间的区分是基础,确保架构支持多样化和优化的处理数据的方式。

IronPDF:C# PDF 库

CQRS Pattern C# (开发人员如何使用):图 3 - IronPDF 网页

探索 IronPDF 进行 PDF 管理 是一个为使用 C# 编程语言的开发人员提供的工具,使他们能够直接在应用程序中创建、读取和编辑 PDF 文档。 该库用户友好,使生成 PDF 报告、发票或 从 HTML 创建 PDF 代码的 PDF 功能集成变得更简单。 IronPDF 支持各种功能,包括编辑 PDF 中的文本和图像、设置文档安全性以及将网页转换为 PDF 格式。 它的多功能性和易用性使其成为希望在项目中实现 PDF 操作的开发人员的宝贵资源。

IronPDF 在其 HTML 转 PDF 转换功能 方面表现出色,保持所有布局和样式不变。 它可以从 web 内容创建 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");
    }
}
$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);
        }
    }
}
$vbLabelText   $csharpLabel

在这个示例中,GeneratePdfReportCommand 代表 CQRS 模式中的一个命令。 它包括一个方法 GenerateReportAsync,接受 reportContent 作为 HTML 字符串,以及一个 outputPath,PDF 报告将保存在此路径下。 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文档。 若要持续使用,许可证从$799起价,为您的项目需求提供多种选择。 IronPdf 提供免费试用版,让您有机会在承诺之前探索其功能。 为便于持续使用,许可证从 $799 开始,提供各种选项以满足您的项目需求。

常见问题解答

什么是软件开发中的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提供免费试用版,允许开发者在购买决策前探索其功能和能力。

Jacob Mellor,Team Iron 的首席技术官
首席技术官

Jacob Mellor 是 Iron Software 的首席技术官,是 C# PDF 技术的先锋工程师。作为 Iron Software 核心代码库的原始开发者,自公司成立以来,他就塑造了公司的产品架构,并与首席执行官 Cameron Rimington 一起将其转变成一家公司,拥有50多人,服务于 NASA、特斯拉和全球政府机构。

Jacob 拥有曼彻斯特大学 (1998-2001) 的一级荣誉土木工程学士学位。1999 年在伦敦创办了自己的第一家软件公司,并于 2005 年创建了他的第一个 .NET 组件后,他专注于解决微软生态系统中的复杂问题。

他的旗舰 IronPDF 和 Iron Suite .NET 库在全球已获得超过 3000 万次的 NuGet 安装,其基础代码继续为全球使用的开发者工具提供支持。拥有 25 年商业经验和 41 年编程经验的 Jacob 仍专注于推动企业级 C#、Java 和 Python PDF 技术的创新,同时指导下一代技术领导者。