Token导航 LogoToken导航TokenDH.com
待分类external-servicegithub未标认证来源可访问许可证需确认审计通过

dotnet-messaging-patternsdotnet 消息传递模式

Agent Skill

dotnet-messaging-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

333

周安装

14

GitHub Stars

15

下载量

116
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-messaging-patterns(dotnet 消息传递模式)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-messaging-patterns
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-messaging-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-messaging-patterns

简介

用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。

  • 适合在需要围绕仓库状态或协作事项进行整理时使用。
  • 可结合来源仓库进一步核验具体用法。dotnet-messaging-patterns 属于待分类类 Skill,可作为该场景下的辅助能力补充。
  • 安装前建议确认权限范围与维护状态。
  • 注意:不涉及背景服务生命周期等内容。

SKILL.md

dotnet-messaging-patterns

Durable messaging patterns for.NET event-driven architectures. Covers publish/subscribe, competing consumers, dead-letter queues, saga/process manager orchestration, and delivery guarantee strategies using Azure Service Bus, RabbitMQ, and MassTransit.

Out of scope: Background service lifecycle and IHostedService registration -- see [skill:dotnet-background-services]. Resilience pipelines and retry policies -- see [skill:dotnet-resilience]. JSON/binary serialization configuration -- see [skill:dotnet-serialization]. In-process producer/consumer queues with Channel<T> -- see [skill:dotnet-channels].

Cross-references: [skill:dotnet-background-services] for hosting message consumers, [skill:dotnet-resilience] for fault tolerance around message handlers, [skill:dotnet-serialization] for message envelope serialization, [skill:dotnet-channels] for in-process queuing patterns.


Messaging Fundamentals

Message Types

TypePurposeExample
CommandRequest an action (one recipient)PlaceOrder, ShipPackage
EventNotify something happened (many recipients)OrderPlaced, PaymentReceived
DocumentTransfer data between systemsCustomerProfile, ProductCatalog

Commands are sent to a specific queue; events are published to a topic/exchange and delivered to all subscribers. This distinction drives the choice between point-to-point and pub/sub topologies.

Delivery Guarantees

GuaranteeBehaviorImplementation
At-most-onceFire and forget; message may be lostNo ack, no retry
At-least-onceMessage retried until acknowledged; duplicates possibleAck after processing + retry on failure
Exactly-onceEach message processed exactly onceAt-least-once + idempotent consumer

At-least-once with idempotent consumers is the standard approach for durable messaging. True exactly-once requires distributed transactions (which most brokers do not support) or consumer-side deduplication.


Publish/Subscribe

Azure Service Bus Topics

// Publisher -- send event to a topic
await using var client = new ServiceBusClient(connectionString);
await using var sender = client.CreateSender("order-events");

var message = new ServiceBusMessage(
    JsonSerializer.SerializeToUtf8Bytes(new OrderPlaced(orderId, total)))
{
    Subject = nameof(OrderPlaced),
    ContentType = "application/json",
    MessageId = Guid.NewGuid().ToString()
};

await sender.SendMessageAsync(message, cancellationToken);
// Subscriber -- process events from a subscription
await using var processor = client.CreateProcessor(
    topicName: "order-events",
    subscriptionName: "billing-service",
    new ServiceBusProcessorOptions
    {
        MaxConcurrentCalls = 10,
        AutoCompleteMessages = false
    });

processor.ProcessMessageAsync += async args =>
{
    var body = args.Message.Body.ToObjectFromJson<OrderPlaced>();
    await HandleOrderPlacedAsync(body);
    await args.CompleteMessageAsync(args.Message);
};

processor.ProcessErrorAsync += args =>
{
    logger.LogError(args.Exception, "Error processing message");
    return Task.CompletedTask;
};

await processor.StartProcessingAsync(cancellationToken);

Key packages:

<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.*" />

RabbitMQ Fanout Exchange

// Publisher -- declare exchange and publish
var factory = new ConnectionFactory { HostName = "localhost" };
await using var connection = await factory.CreateConnectionAsync();
await using var channel = await connection.CreateChannelAsync();

await channel.ExchangeDeclareAsync(
    exchange: "order-events",
    type: ExchangeType.Fanout,
    durable: true);

var body = JsonSerializer.SerializeToUtf8Bytes(
    new OrderPlaced(orderId, total));

await channel.BasicPublishAsync(
    exchange: "order-events",
    routingKey: string.Empty,
    body: body);

Key packages:

<PackageReference Include="RabbitMQ.Client" Version="7.*" />

MassTransit Publish

MassTransit abstracts the broker, providing a unified API for Azure Service Bus, RabbitMQ, Amazon SQS, and in-memory transport.

// Registration
builder.Services.AddMassTransit(x =>
{
    x.AddConsumer<OrderPlacedConsumer>();

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("localhost", "/", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });
        cfg.ConfigureEndpoints(context);
    });
});

// Publisher
public sealed class OrderService(IPublishEndpoint publishEndpoint)
{
    public async Task PlaceOrderAsync(
        Guid orderId, decimal total, CancellationToken ct)
    {
        // Process order...
        await publishEndpoint.Publish(
            new OrderPlaced(orderId, total), ct);
    }
}

// Consumer
public sealed class OrderPlacedConsumer(
    ILogger<OrderPlacedConsumer> logger)
    : IConsumer<OrderPlaced>
{
    public async Task Consume(ConsumeContext<OrderPlaced> context)
    {
        logger.LogInformation(
            "Processing order {OrderId}", context.Message.OrderId);
        await ProcessAsync(context.Message);
    }
}

// Message contract (use records in a shared contracts assembly)
public record OrderPlaced(Guid OrderId, decimal Total);

Key packages:

<PackageReference Include="MassTransit" Version="8.*" />
<!-- Pick ONE transport: -->
<PackageReference Include="MassTransit.RabbitMQ" Version="8.*" />
<!-- OR -->
<PackageReference Include="MassTransit.Azure.ServiceBus.Core" Version="8.*" />

Competing Consumers

Multiple consumer instances process messages from the same queue in parallel. The broker delivers each message to exactly one consumer, distributing load across instances.

Pattern

Queue: order-processing
  ├── Consumer Instance A  (picks message 1)
  ├── Consumer Instance B  (picks message 2)
  └── Consumer Instance C  (picks message 3)

Azure Service Bus -- Scaling Consumers

// Multiple instances reading from the same queue automatically compete.
// MaxConcurrentCalls controls per-instance parallelism.
var processor = client.CreateProcessor("order-processing",
    new ServiceBusProcessorOptions
    {
        MaxConcurrentCalls = 20,
        PrefetchCount = 50,
        AutoCompleteMessages = false
    });

MassTransit -- Concurrency Limits

x.AddConsumer<OrderProcessor>(cfg =>
{
    cfg.UseConcurrentMessageLimit(10);
});

Ordering Considerations

Competing consumers sacrifice strict ordering for throughput. When order matters:

  • Azure Service Bus: Use sessions (RequiresSession = true) to guarantee FIFO within a session ID (e.g., per customer)
  • RabbitMQ: Use a single consumer per queue, or consistent-hash exchange to partition by key
  • MassTransit: Configure UseMessagePartitioner for key-based ordering

Dead-Letter Queues

Dead-letter queues (DLQs) capture messages that cannot be processed after exhausting retries. They prevent poison messages from blocking the main queue.

Why Messages Are Dead-Lettered

ReasonTrigger
Max delivery attempts exceededMessage failed processing N times
TTL expiredMessage sat in queue past its time-to-live
Consumer rejectionConsumer explicitly dead-letters the message
Queue length exceededQueue overflow policy routes to DLQ

Azure Service Bus DLQ

// Dead-letter a message with reason
await args.DeadLetterMessageAsync(
    args.Message,
    deadLetterReason: "ValidationFailed",
    deadLetterErrorDescription: "Missing required field: CustomerId");

// Read from the dead-letter sub-queue
await using var dlqReceiver = client.CreateReceiver(
    "order-processing",
    new ServiceBusReceiverOptions
    {
        SubQueue = SubQueue.DeadLetter
    });

while (true)
{
    var message = await dlqReceiver.ReceiveMessageAsync(
        TimeSpan.FromSeconds(5), cancellationToken);
    if (message is null) break;

    logger.LogWarning(
        "DLQ message: {Reason} - {Description}",
        message.DeadLetterReason,
        message.DeadLetterErrorDescription);

    // Inspect, fix, and re-submit or discard
    await dlqReceiver.CompleteMessageAsync(message);
}

MassTransit Error/Fault Queues

MassTransit automatically creates _error and _skipped queues. Failed messages after retry exhaustion move to the error queue with fault metadata.

// Configure retry before dead-lettering
x.AddConsumer<OrderProcessor>(cfg =>
{
    cfg.UseMessageRetry(r => r.Intervals(
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5),
        TimeSpan.FromSeconds(15)));
});

DLQ Monitoring

Always monitor DLQ depth with alerts. Unmonitored DLQs accumulate silently until data is lost or stale.


Saga / Process Manager

Sagas coordinate multi-step business processes across services. Each step publishes events that trigger the next step, with compensation logic for failures.

Choreography vs Orchestration

StyleHow it worksUse when
ChoreographyServices react to events independently; no central coordinatorSimple flows, few steps, loosely coupled
OrchestrationA saga/process manager directs each stepComplex flows, compensation needed, visibility required

MassTransit State Machine Saga

// Saga state
public class OrderState : SagaStateMachineInstance
{
    public Guid CorrelationId { get; set; }
    public string CurrentState { get; set; } = default!;
    public Guid OrderId { get; set; }
    public decimal Total { get; set; }
    public DateTime? PaymentReceivedAt { get; set; }
}

// State machine definition
public sealed class OrderStateMachine : MassTransitStateMachine<OrderState>
{
    public State Submitted { get; private set; } = default!;
    public State PaymentPending { get; private set; } = default!;
    public State Completed { get; private set; } = default!;
    public State Faulted { get; private set; } = default!;

    public Event<OrderSubmitted> OrderSubmitted { get; private set; } = default!;
    public Event<PaymentReceived> PaymentReceived { get; private set; } = default!;
    public Event<PaymentFailed> PaymentFailed { get; private set; } = default!;

    public OrderStateMachine()
    {
        InstanceState(x => x.CurrentState);

        Event(() => OrderSubmitted,
            x => x.CorrelateById(ctx => ctx.Message.OrderId));
        Event(() => PaymentReceived,
            x => x.CorrelateById(ctx => ctx.Message.OrderId));
        Event(() => PaymentFailed,
            x => x.CorrelateById(ctx => ctx.Message.OrderId));

        Initially(
            When(OrderSubmitted)
                .Then(ctx =>
                {
                    ctx.Saga.OrderId = ctx.Message.OrderId;
                    ctx.Saga.Total = ctx.Message.Total;
                })
                .Publish(ctx => new RequestPayment(
                    ctx.Saga.OrderId, ctx.Saga.Total))
                .TransitionTo(PaymentPending));

        During(PaymentPending,
            When(PaymentReceived)
                .Then(ctx =>
                    ctx.Saga.PaymentReceivedAt = DateTime.UtcNow)
                .Publish(ctx => new FulfillOrder(ctx.Saga.OrderId))
                .TransitionTo(Completed),
            When(PaymentFailed)
                .Publish(ctx => new CancelOrder(ctx.Saga.OrderId))
                .TransitionTo(Faulted));
    }
}

// Registration -- requires MassTransit.EntityFrameworkCore package for EF persistence
// NuGet: MassTransit.EntityFrameworkCore Version="8.*"
builder.Services.AddMassTransit(x =>
{
    x.AddSagaStateMachine<OrderStateMachine, OrderState>()
        .EntityFrameworkRepository(r =>
        {
            r.ExistingDbContext<SagaDbContext>();
            r.UsePostgres();
        });

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
    });
});

Saga Persistence

StorePackageUse when
Entity Framework CoreMassTransit.EntityFrameworkCoreAlready using EF Core; need transactions
MongoDBMassTransit.MongoDbDocument-oriented state; high throughput
RedisMassTransit.RedisEphemeral sagas; low latency
In-MemoryBuilt-inTesting only -- state lost on restart

Compensation Pattern

When a saga step fails, publish compensating commands to undo prior steps:

OrderSubmitted -> RequestPayment -> PaymentReceived -> ReserveInventory
                                                          |
                                                     InventoryFailed
                                                          |
                                                    RefundPayment (compensation)
                                                          |
                                                    CancelOrder (compensation)

Idempotent Consumers

At-least-once delivery means consumers may receive the same message multiple times. Idempotent consumers ensure repeated processing produces the same result.

Database-Based Deduplication

public sealed class IdempotentOrderConsumer(
    AppDbContext db,
    ILogger<IdempotentOrderConsumer> logger)
    : IConsumer<OrderPlaced>
{
    public async Task Consume(ConsumeContext<OrderPlaced> context)
    {
        var messageId = context.MessageId
            ?? throw new InvalidOperationException("Missing MessageId");

        // Check if already processed
        var exists = await db.ProcessedMessages
            .AnyAsync(m => m.MessageId == messageId);

        if (exists)
        {
            logger.LogInformation(
                "Duplicate message {MessageId}, skipping", messageId);
            return;
        }

        // Process the message
        await ProcessOrderAsync(context.Message);

        // Record as processed
        db.ProcessedMessages.Add(new ProcessedMessage
        {
            MessageId = messageId,
            ProcessedAt = DateTime.UtcNow,
            ConsumerType = nameof(IdempotentOrderConsumer)
        });

        await db.SaveChangesAsync();
    }
}

Natural Idempotency

Prefer operations that are naturally idempotent:

  • Upserts (INSERT... ON CONFLICT UPDATE) instead of blind inserts
  • Conditional updates (UPDATE... WHERE Status = 'Pending') instead of unconditional
  • Deterministic IDs derived from message content instead of auto-generated

Message Envelope Pattern

Wrap message payloads in a standard envelope with metadata for tracing, versioning, and routing.

public sealed record MessageEnvelope<T>(
    string MessageId,
    string MessageType,
    DateTimeOffset Timestamp,
    string CorrelationId,
    string Source,
    int Version, // Schema version for backward-compatible deserialization
    T Payload);

MassTransit provides this automatically via ConsumeContext (MessageId, CorrelationId, Headers). When using raw broker clients, implement envelopes explicitly.


Agent Gotchas

  1. Do not use auto-complete with Azure Service Bus -- set AutoCompleteMessages = false and call CompleteMessageAsync after successful processing. Auto-complete acknowledges before processing finishes, risking data loss on failure.
  2. Do not forget to handle poison messages -- always configure max delivery count and DLQ monitoring. Without these, a single bad message blocks the entire queue indefinitely.
  3. Do not use in-memory saga persistence in production -- saga state is lost on restart, leaving business processes in unknown states. Use Entity Framework, MongoDB, or Redis persistence.
  4. Do not assume message ordering across partitions -- competing consumers and topic subscriptions deliver messages out of order by default. Use sessions or partitioning when order matters.
  5. Do not skip idempotency for at-least-once consumers -- brokers may redeliver on timeout, network glitch, or consumer restart. Every consumer must handle duplicate messages safely.
  6. Do not hardcode connection strings -- use environment variables or Azure Key Vault references. For local development, use user secrets or .env files excluded from source control.

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

34%
按下载量换算39

Claude

32.14%
按下载量换算37

Cursor

16.59%
按下载量换算19

Gemini CLI

9.68%
按下载量换算11

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills