mcpField
A
azure-eventgrid-dotnet

azure-eventgrid-dotnet

github.com/sickn33云与 DevOps48.7k56 次安装今天更新
代码仓库

关于 azure-eventgrid-dotnet

azure-eventgrid-dotnet 是用于通过 .NET 客户端库发布和接收 Azure Event Grid 事件的 SDK,适合构建事件驱动应用的开发者。它覆盖主题和域的推送投递,以及命名空间的拉取投递,并支持 EventGridEvent 与 CloudEvent 格式。

主要功能

  1. 01向主题和域发布事件
  2. 02命名空间事件发送与接收
  3. 03支持 EventGridEvent 和 CloudEvent
  4. 04批量发送与事件解析
  5. 05支持 API 密钥、SAS 和 Microsoft Entra ID

适用场景

  1. 01发布业务事件到 Event Grid 主题
  2. 02通过命名空间订阅拉取和处理事件
  3. 03解析 webhook 负载或 CloudEvents

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill azure-eventgrid-dotnet

安装后在 Claude Code 中输入/azure-eventgrid-dotnet确认已加载。

详细介绍

Azure.Messaging.EventGrid(.NET)

用于向 Azure Event Grid 主题、域和命名空间发布事件的客户端库。

安装

# For topics and domains (push delivery)
dotnet add package Azure.Messaging.EventGrid

# For namespaces (pull delivery)
dotnet add package Azure.Messaging.EventGrid.Namespaces

# For CloudNative CloudEvents interop
dotnet add package Microsoft.Azure.Messaging.EventGrid.CloudNativeCloudEvents

历史示例版本:4.28.0;请验证并固定兼容的 SDK/Functions 包版本。

环境变量

# Topic/Domain endpoint
EVENT_GRID_TOPIC_ENDPOINT=https://<topic-name>.<region>.eventgrid.azure.net/api/events
EVENT_GRID_TOPIC_KEY=<access-key>

# Namespace endpoint (for pull delivery)
EVENT_GRID_NAMESPACE_ENDPOINT=https://<namespace>.<region>.eventgrid.azure.net
EVENT_GRID_TOPIC_NAME=<topic-name>
EVENT_GRID_SUBSCRIPTION_NAME=<subscription-name>

客户端层级

Push Delivery (Topics/Domains)
└── EventGridPublisherClient
    ├── SendEventAsync(EventGridEvent)
    ├── SendEventsAsync(IEnumerable<EventGridEvent>)
    ├── SendEventAsync(CloudEvent)
    └── SendEventsAsync(IEnumerable<CloudEvent>)

Pull Delivery (Namespaces)
├── EventGridSenderClient
│   └── SendAsync(CloudEvent)
└── EventGridReceiverClient
    ├── ReceiveAsync()
    ├── AcknowledgeAsync()
    ├── ReleaseAsync()
    └── RejectAsync()

身份验证

API 密钥身份验证
using Azure;
using Azure.Messaging.EventGrid;

EventGridPublisherClient client = new(
    new Uri("https://mytopic.eastus-1.eventgrid.azure.net/api/events"),
    new AzureKeyCredential("<access-key>"));
Microsoft Entra ID(推荐)
using Azure.Identity;
using Azure.Messaging.EventGrid;

EventGridPublisherClient client = new(
    new Uri("https://mytopic.eastus-1.eventgrid.azure.net/api/events"),
    new DefaultAzureCredential());
SAS 令牌身份验证
string sasToken = EventGridPublisherClient.BuildSharedAccessSignature(
    new Uri(topicEndpoint),
    DateTimeOffset.UtcNow.AddHours(1),
    new AzureKeyCredential(topicKey));

var sasCredential = new AzureSasCredential(sasToken);
EventGridPublisherClient client = new(
    new Uri(topicEndpoint),
    sasCredential);

发布事件

EventGridEvent 架构
EventGridPublisherClient client = new(
    new Uri(topicEndpoint),
    new AzureKeyCredential(topicKey));

// Single event
EventGridEvent egEvent = new(
    subject: "orders/12345",
    eventType: "Order.Created",
    dataVersion: "1.0",
    data: new { OrderId = "12345", Amount = 99.99 });

await client.SendEventAsync(egEvent);

// Batch of events
List<EventGridEvent> events = new()
{
    new EventGridEvent(
        subject: "orders/12345",
        eventType: "Order.Created",
        dataVersion: "1.0",
        data: new OrderData { OrderId = "12345", Amount = 99.99 }),
    new EventGridEvent(
        subject: "orders/12346",
        eventType: "Order.Created",
        dataVersion: "1.0",
        data: new OrderData { OrderId = "12346", Amount = 149.99 })
};

await client.SendEventsAsync(events);
CloudEvent 架构
CloudEvent cloudEvent = new(
    source: "/orders/system",
    type: "Order.Created",
    data: new { OrderId = "12345", Amount = 99.99 });

cloudEvent.Subject = "orders/12345";
cloudEvent.Id = Guid.NewGuid().ToString();
cloudEvent.Time = DateTimeOffset.UtcNow;

await client.SendEventAsync(cloudEvent);

// Batch of CloudEvents
List<CloudEvent> cloudEvents = new()
{
    new CloudEvent("/orders", "Order.Created", new { OrderId = "1" }),
    new CloudEvent("/orders", "Order.Updated", new { OrderId = "2" })
};

await client.SendEventsAsync(cloudEvents);
发布到 Event Grid 域
// Events must specify the Topic property for domain routing
List<EventGridEvent> events = new()
{
    new EventGridEvent(
        subject: "orders/12345",
        eventType: "Order.Created",
        dataVersion: "1.0",
        data: new { OrderId = "12345" })
    {
        Topic = "orders-topic"  // Domain topic name
    },
    new EventGridEvent(
        subject: "inventory/item-1",
        eventType: "Inventory.Updated",
        dataVersion: "1.0",
        data: new { ItemId = "item-1" })
    {
        Topic = "inventory-topic"
    }
};

await client.SendEventsAsync(events);
自定义序列化
using System.Text.Json;
using Azure.Core.Serialization;

var serializerOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

var customSerializer = new JsonObjectSerializer(serializerOptions);

EventGridEvent egEvent = new(
    subject: "orders/12345",
    eventType: "Order.Created",
    dataVersion: "1.0",
    data: customSerializer.Serialize(new OrderData { OrderId = "12345" }));

await client.SendEventAsync(egEvent);

拉取传递(命名空间)

向命名空间主题发送事件
using Azure;
using Azure.Messaging;
using Azure.Messaging.EventGrid.Namespaces;

var senderClient = new EventGridSenderClient(
    new Uri(namespaceEndpoint),
    topicName,
    new AzureKeyCredential(topicKey));

// Send single event
CloudEvent cloudEvent = new("employee_source", "Employee.Created", 
    new { Name = "John", Age = 30 });
await senderClient.SendAsync(cloudEvent);

// Send batch
await senderClient.SendAsync(new[]
{
    new CloudEvent("source", "type", new { Name = "Alice" }),
    new CloudEvent("source", "type", new { Name = "Bob" })
});
接收并处理事件
var receiverClient = new EventGridReceiverClient(
    new Uri(namespaceEndpoint),
    topicName,
    subscriptionName,
    new AzureKeyCredential(topicKey));

// Receive events
ReceiveResult result = await receiverClient.ReceiveAsync(maxEvents: 10);

List<string> lockTokensToAck = new();
List<string> lockTokensToRelease = new();

foreach (ReceiveDetails detail in result.Details)
{
    CloudEvent cloudEvent = detail.Event;
    string lockToken = detail.BrokerProperties.LockToken;
    
    try
    {
        // Process the event
        await ProcessEventIdempotentlyAsync(cloudEvent); // application helper; durable success before ack
        lockTokensToAck.Add(lockToken);
    }
    catch (Exception)
    {
        // Release for retry
        lockTokensToRelease.Add(lockToken);
    }
}

// Acknowledge successfully processed events
if (lockTokensToAck.Any())
{
    await receiverClient.AcknowledgeAsync(lockTokensToAck);
}

// Release events for retry
if (lockTokensToRelease.Any())
{
    await receiverClient.ReleaseAsync(lockTokensToRelease);
}
拒绝事件(死信行为取决于订阅配置)
// Reject events that cannot be processed
await receiverClient.RejectAsync(new[] { lockToken });

使用事件(Azure Functions)

EventGridEvent 触发器
using Azure.Messaging.EventGrid;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;

public static class EventGridFunction
{
    [FunctionName("ProcessEventGridEvent")]
    public static void Run(
        [EventGridTrigger] EventGridEvent eventGridEvent,
        ILogger log)
    {
        log.LogInformation($"Event Type: {eventGridEvent.EventType}");
        log.LogInformation($"Subject: {eventGridEvent.Subject}");
        log.LogInformation($"Data: {eventGridEvent.Data}");
    }
}
CloudEvent 触发器
using Azure.Messaging;
using Microsoft.Azure.Functions.Worker;

public class CloudEventFunction
{
    [Function("ProcessCloudEvent")]
    public void Run(
        [EventGridTrigger] CloudEvent cloudEvent,
        FunctionContext context)
    {
        var logger = context.GetLogger("ProcessCloudEvent");
        logger.LogInformation($"Event Type: {cloudEvent.Type}");
        logger.LogInformation($"Source: {cloudEvent.Source}");
        logger.LogInformation($"Data: {cloudEvent.Data}");
    }
}

解析事件

解析 EventGridEvent
// From JSON string
string json = "..."; // Event Grid webhook payload
EventGridEvent[] events = EventGridEvent.ParseMany(BinaryData.FromString(json));

foreach (EventGridEvent egEvent in events)
{
    if (egEvent.TryGetSystemEventData(out object systemEvent))
    {
        // Handle system event
        switch (systemEvent)
        {
            case StorageBlobCreatedEventData blobCreated:
                Console.WriteLine($"Blob created: {blobCreated.Url}");
                break;
        }
    }
    else
    {
        // Handle custom event
        var customData = egEvent.Data.ToObjectFromJson<MyCustomData>();
    }
}
解析 CloudEvent
CloudEvent[] cloudEvents = CloudEvent.ParseMany(BinaryData.FromString(json));

foreach (CloudEvent cloudEvent in cloudEvents)
{
    var data = cloudEvent.Data.ToObjectFromJson<MyEventData>();
    Console.WriteLine($"Type: {cloudEvent.Type}, Data: {data}");
}

系统事件

// Common system event types
using Azure.Messaging.EventGrid.SystemEvents;

// Storage events
StorageBlobCreatedEventData blobCreated;
StorageBlobDeletedEventData blobDeleted;

// Resource events
ResourceWriteSuccessEventData resourceCreated;
ResourceDeleteSuccessEventData resourceDeleted;

// App Service events
WebAppUpdatedEventData webAppUpdated;

// Container Registry events
ContainerRegistryImagePushedEventData imagePushed;

// IoT Hub events
IotHubDeviceCreatedEventData deviceCreated;

关键类型参考

类型用途
EventGridPublisherClient发布到主题/域
EventGridSenderClient向命名空间主题发送
EventGridReceiverClient接收命名空间订阅中的事件
EventGridEventEvent Grid 原生架构
CloudEventCloudEvents 1.0 架构
ReceiveResult拉取传递响应
ReceiveDetails包含代理属性的事件
BrokerProperties锁定令牌、传递次数

事件架构比较

特性EventGridEventCloudEvent
标准Azure 专用CNCF 标准
必填字段验证所选的 Event Grid 架构specversion、id、source、type(SDK 可能会初始化其中一些字段)
可扩展性有限扩展属性
互操作性仅限 Azure跨平台

最佳实践

  1. 使用 CloudEvents — 新实现优先使用 CloudEvents(行业标准)
  2. 批量发送事件 — 在一次调用中发送多个事件以提高效率
  3. 使用 Entra ID — 优先使用托管标识,而非访问密钥
  4. 使用幂等处理程序 — 事件可能会被传递多次
  5. 设置事件 TTL — 为命名空间事件配置生存时间
  6. 处理部分失败 — 分别确认/释放事件
  7. 使用死信 — 为失败的事件配置死信
  8. 验证架构 — 在处理前验证事件数据

错误处理

using Azure;

try
{
    await client.SendEventAsync(cloudEvent);
}
catch (RequestFailedException ex) when (ex.Status == 401)
{
    Console.WriteLine("Authentication failed - check credentials");
}
catch (RequestFailedException ex) when (ex.Status == 403)
{
    Console.WriteLine("Authorization failed - check RBAC permissions");
}
catch (RequestFailedException ex) when (ex.Status == 413)
{
    Console.WriteLine("Payload too large - max 1MB per event, 1MB total batch");
}
catch (RequestFailedException ex)
{
    Console.WriteLine($"Event Grid error: {ex.Status} - {ex.Message}");
}

故障转移模式

try
{
    var primaryClient = new EventGridPublisherClient(primaryUri, primaryKey);
    await primaryClient.SendEventsAsync(events);
}
catch (RequestFailedException ex) when (ex.Status >= 500 || ex.Status == 408)
{
    // Explicitly configured failover only; primary acceptance may be unknown.
    // Failover to secondary region
    var secondaryClient = new EventGridPublisherClient(secondaryUri, secondaryKey);
    await secondaryClient.SendEventsAsync(events);
}

相关 SDK

SDK用途安装
Azure.Messaging.EventGrid主题/域(此 SDK)dotnet add package Azure.Messaging.EventGrid
Azure.Messaging.EventGrid.Namespaces拉取传递dotnet add package Azure.Messaging.EventGrid.Namespaces
Azure.Identity身份验证dotnet add package Azure.Identity
Microsoft.Azure.WebJobs.Extensions.EventGridAzure Functions 触发器dotnet add package Microsoft.Azure.WebJobs.Extensions.EventGrid

参考链接

资源URL
NuGet 包https://www.nuget.org/packages/Azure.Messaging.EventGrid
API 参考https://learn.microsoft.com/dotnet/api/azure.messaging.eventgrid
快速入门https://learn.microsoft.com/azure/event-grid/custom-event-quickstart
拉取传递https://learn.microsoft.com/azure/event-grid/pull-delivery-overview
GitHub 源代码https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/eventgrid/Azure.Messaging.EventGrid

适用场景

当需要执行概述中描述的工作流或操作时,可使用此技能。

限制

  • 仅当任务明确符合上述范围时,才使用此技能。
  • 不要将输出视为环境特定验证、测试或专家审查的替代品。
  • 如果缺少必需的输入、权限、安全边界或成功标准,请暂停并寻求澄清。

代码片段是独立片段,并非编译后的应用程序。请使用实际的主题输入架构、终结点和订阅模式;不要混用 Functions 进程内和隔离工作进程的导入项或软件包。保护事件负载以及 SAS/锁令牌,避免将其写入日志。请核实每个事件的确认失败和锁过期情况;如果未配置死信,拒绝操作可能会丢弃事件。跨区域发布可能导致已接受的事件重复,且不会转移订阅或保证顺序;请保留稳定的事件 ID 并进行持久化去重。在处理前,请验证 webhook 身份验证、订阅质询和架构。限制因主题/命名空间 API 而异,必须加以核实。

评论

支持 Markdown