--- 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. Triggers: "Event Grid", "EventGridPublisherClient", "CloudEvent", "EventGridEvent", "publish events .NET", "event-driven", "pub/sub". package: Azure.Messaging.EventGrid --- # 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://..eventgrid.azure.net/api/events EVENT_GRID_TOPIC_KEY= # Namespace endpoint (for pull delivery) EVENT_GRID_NAMESPACE_ENDPOINT=https://..eventgrid.azure.net EVENT_GRID_TOPIC_NAME= EVENT_GRID_SUBSCRIPTION_NAME= ``` ## Client Hierarchy ``` Push Delivery (Topics/Domains) └── EventGridPublisherClient ├── SendEventAsync(EventGridEvent) ├── SendEventsAsync(IEnumerable) ├── SendEventAsync(CloudEvent) └── SendEventsAsync(IEnumerable) 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("")); ``` ### 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 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 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 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 lockTokensToAck = new(); List 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(); } } ``` ### Parse CloudEvent ```csharp CloudEvent[] cloudEvents = CloudEvent.ParseMany(BinaryData.FromString(json)); foreach (CloudEvent cloudEvent in cloudEvents) { var data = cloudEvent.Data.ToObjectFromJson(); 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 |