* chore: upgrade maintenance scripts to robust PyYAML parsing - Replaces fragile regex frontmatter parsing with PyYAML/yaml library - Ensures multi-line descriptions and complex characters are handled safely - Normalizes quoting and field ordering across all maintenance scripts - Updates validator to strictly enforce description quality * fix: restore and refine truncated skill descriptions - Recovered 223+ truncated descriptions from git history (6.5.0 regression) - Refined long descriptions into concise, complete sentences (<200 chars) - Added missing descriptions for brainstorming and orchestration skills - Manually fixed imagen skill description - Resolved dangling links in competitor-alternatives skill * chore: sync generated registry files and document fixes - Regenerated skills index with normalized forward-slash paths - Updated README and CATALOG to reflect restored descriptions - Documented restoration and script improvements in CHANGELOG.md * fix: restore missing skill and align metadata for full 955 count - Renamed SKILL.MD to SKILL.md in andruia-skill-smith to ensure indexing - Fixed risk level and missing section in andruia-skill-smith - Synchronized all registry files for final 955 skill count * chore(scripts): add cross-platform runners and hermetic test orchestration * fix(scripts): harden utf-8 output and clone target writeability * fix(skills): add missing date metadata for strict validation * chore(index): sync generated metadata dates * fix(catalog): normalize skill paths to prevent CI drift * chore: sync generated registry files * fix: enforce LF line endings for generated registry files
493 lines
13 KiB
Markdown
493 lines
13 KiB
Markdown
---
|
|
name: azure-eventgrid-dotnet
|
|
description: Azure Event Grid SDK for .NET. Client library for publishing and consuming events with Azure Event Grid. Use for event-driven architectures, pub/sub messaging, CloudEvents, and EventGridEvents.
|
|
risk: unknown
|
|
source: community
|
|
date_added: '2026-02-27'
|
|
---
|
|
|
|
# Azure.Messaging.EventGrid (.NET)
|
|
|
|
Client library for publishing events to Azure Event Grid topics, domains, and namespaces.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# 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
|
|
```
|
|
|
|
**Current Version**: 4.28.0 (stable)
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# 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>
|
|
```
|
|
|
|
## Client Hierarchy
|
|
|
|
```
|
|
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()
|
|
```
|
|
|
|
## Authentication
|
|
|
|
### API Key Authentication
|
|
|
|
```csharp
|
|
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 (Recommended)
|
|
|
|
```csharp
|
|
using Azure.Identity;
|
|
using Azure.Messaging.EventGrid;
|
|
|
|
EventGridPublisherClient client = new(
|
|
new Uri("https://mytopic.eastus-1.eventgrid.azure.net/api/events"),
|
|
new DefaultAzureCredential());
|
|
```
|
|
|
|
### SAS Token Authentication
|
|
|
|
```csharp
|
|
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);
|
|
```
|
|
|
|
## Publishing Events
|
|
|
|
### EventGridEvent Schema
|
|
|
|
```csharp
|
|
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 Schema
|
|
|
|
```csharp
|
|
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);
|
|
```
|
|
|
|
### Publishing to Event Grid Domain
|
|
|
|
```csharp
|
|
// 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);
|
|
```
|
|
|
|
### Custom Serialization
|
|
|
|
```csharp
|
|
using System.Text.Json;
|
|
|
|
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);
|
|
```
|
|
|
|
## Pull Delivery (Namespaces)
|
|
|
|
### Send Events to Namespace Topic
|
|
|
|
```csharp
|
|
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" })
|
|
});
|
|
```
|
|
|
|
### Receive and Process Events
|
|
|
|
```csharp
|
|
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
|
|
Console.WriteLine($"Event: {cloudEvent.Type}, Data: {cloudEvent.Data}");
|
|
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 (Dead Letter)
|
|
|
|
```csharp
|
|
// Reject events that cannot be processed
|
|
await receiverClient.RejectAsync(new[] { lockToken });
|
|
```
|
|
|
|
## Consuming Events (Azure Functions)
|
|
|
|
### EventGridEvent Trigger
|
|
|
|
```csharp
|
|
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 Trigger
|
|
|
|
```csharp
|
|
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}");
|
|
}
|
|
}
|
|
```
|
|
|
|
## Parsing Events
|
|
|
|
### Parse EventGridEvent
|
|
|
|
```csharp
|
|
// 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>();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Parse CloudEvent
|
|
|
|
```csharp
|
|
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}");
|
|
}
|
|
```
|
|
|
|
## System Events
|
|
|
|
```csharp
|
|
// 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;
|
|
```
|
|
|
|
## Key Types Reference
|
|
|
|
| Type | Purpose |
|
|
|------|---------|
|
|
| `EventGridPublisherClient` | Publish to topics/domains |
|
|
| `EventGridSenderClient` | Send to namespace topics |
|
|
| `EventGridReceiverClient` | Receive from namespace subscriptions |
|
|
| `EventGridEvent` | Event Grid native schema |
|
|
| `CloudEvent` | CloudEvents 1.0 schema |
|
|
| `ReceiveResult` | Pull delivery response |
|
|
| `ReceiveDetails` | Event with broker properties |
|
|
| `BrokerProperties` | Lock token, delivery count |
|
|
|
|
## Event Schemas Comparison
|
|
|
|
| Feature | EventGridEvent | CloudEvent |
|
|
|---------|----------------|------------|
|
|
| Standard | Azure-specific | CNCF standard |
|
|
| Required fields | subject, eventType, dataVersion, data | source, type |
|
|
| Extensibility | Limited | Extension attributes |
|
|
| Interoperability | Azure only | Cross-platform |
|
|
|
|
## Best Practices
|
|
|
|
1. **Use CloudEvents** — Prefer CloudEvents for new implementations (industry standard)
|
|
2. **Batch events** — Send multiple events in one call for efficiency
|
|
3. **Use Entra ID** — Prefer managed identity over access keys
|
|
4. **Idempotent handlers** — Events may be delivered more than once
|
|
5. **Set event TTL** — Configure time-to-live for namespace events
|
|
6. **Handle partial failures** — Acknowledge/release events individually
|
|
7. **Use dead-letter** — Configure dead-letter for failed events
|
|
8. **Validate schemas** — Validate event data before processing
|
|
|
|
## Error Handling
|
|
|
|
```csharp
|
|
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}");
|
|
}
|
|
```
|
|
|
|
## Failover Pattern
|
|
|
|
```csharp
|
|
try
|
|
{
|
|
var primaryClient = new EventGridPublisherClient(primaryUri, primaryKey);
|
|
await primaryClient.SendEventsAsync(events);
|
|
}
|
|
catch (RequestFailedException)
|
|
{
|
|
// Failover to secondary region
|
|
var secondaryClient = new EventGridPublisherClient(secondaryUri, secondaryKey);
|
|
await secondaryClient.SendEventsAsync(events);
|
|
}
|
|
```
|
|
|
|
## Related SDKs
|
|
|
|
| SDK | Purpose | Install |
|
|
|-----|---------|---------|
|
|
| `Azure.Messaging.EventGrid` | Topics/Domains (this SDK) | `dotnet add package Azure.Messaging.EventGrid` |
|
|
| `Azure.Messaging.EventGrid.Namespaces` | Pull delivery | `dotnet add package Azure.Messaging.EventGrid.Namespaces` |
|
|
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
|
| `Microsoft.Azure.WebJobs.Extensions.EventGrid` | Azure Functions trigger | `dotnet add package Microsoft.Azure.WebJobs.Extensions.EventGrid` |
|
|
|
|
## Reference Links
|
|
|
|
| Resource | URL |
|
|
|----------|-----|
|
|
| NuGet Package | https://www.nuget.org/packages/Azure.Messaging.EventGrid |
|
|
| API Reference | https://learn.microsoft.com/dotnet/api/azure.messaging.eventgrid |
|
|
| Quickstart | https://learn.microsoft.com/azure/event-grid/custom-event-quickstart |
|
|
| Pull Delivery | https://learn.microsoft.com/azure/event-grid/pull-delivery-overview |
|
|
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/eventgrid/Azure.Messaging.EventGrid |
|
|
|
|
## When to Use
|
|
This skill is applicable to execute the workflow or actions described in the overview.
|