关于 azure-eventgrid-dotnet
azure-eventgrid-dotnet 是用于通过 .NET 客户端库发布和接收 Azure Event Grid 事件的 SDK,适合构建事件驱动应用的开发者。它覆盖主题和域的推送投递,以及命名空间的拉取投递,并支持 EventGridEvent 与 CloudEvent 格式。
主要功能
- 01向主题和域发布事件
- 02命名空间事件发送与接收
- 03支持 EventGridEvent 和 CloudEvent
- 04批量发送与事件解析
- 05支持 API 密钥、SAS 和 Microsoft Entra ID
适用场景
- 01发布业务事件到 Event Grid 主题
- 02通过命名空间订阅拉取和处理事件
- 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 | 接收命名空间订阅中的事件 |
EventGridEvent | Event Grid 原生架构 |
CloudEvent | CloudEvents 1.0 架构 |
ReceiveResult | 拉取传递响应 |
ReceiveDetails | 包含代理属性的事件 |
BrokerProperties | 锁定令牌、传递次数 |
事件架构比较
| 特性 | EventGridEvent | CloudEvent |
|---|---|---|
| 标准 | Azure 专用 | CNCF 标准 |
| 必填字段 | 验证所选的 Event Grid 架构 | specversion、id、source、type(SDK 可能会初始化其中一些字段) |
| 可扩展性 | 有限 | 扩展属性 |
| 互操作性 | 仅限 Azure | 跨平台 |
最佳实践
- 使用 CloudEvents — 新实现优先使用 CloudEvents(行业标准)
- 批量发送事件 — 在一次调用中发送多个事件以提高效率
- 使用 Entra ID — 优先使用托管标识,而非访问密钥
- 使用幂等处理程序 — 事件可能会被传递多次
- 设置事件 TTL — 为命名空间事件配置生存时间
- 处理部分失败 — 分别确认/释放事件
- 使用死信 — 为失败的事件配置死信
- 验证架构 — 在处理前验证事件数据
错误处理
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.EventGrid | Azure Functions 触发器 | dotnet add package Microsoft.Azure.WebJobs.Extensions.EventGrid |
参考链接
适用场景
当需要执行概述中描述的工作流或操作时,可使用此技能。
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必需的输入、权限、安全边界或成功标准,请暂停并寻求澄清。
代码片段是独立片段,并非编译后的应用程序。请使用实际的主题输入架构、终结点和订阅模式;不要混用 Functions 进程内和隔离工作进程的导入项或软件包。保护事件负载以及 SAS/锁令牌,避免将其写入日志。请核实每个事件的确认失败和锁过期情况;如果未配置死信,拒绝操作可能会丢弃事件。跨区域发布可能导致已接受的事件重复,且不会转移订阅或保证顺序;请保留稳定的事件 ID 并进行持久化去重。在处理前,请验证 webhook 身份验证、订阅质询和架构。限制因主题/命名空间 API 而异,必须加以核实。
评论
支持 Markdown