feat: Add Official Microsoft & Gemini Skills (845+ Total)
🚀 Impact Significantly expands the capabilities of **Antigravity Awesome Skills** by integrating official skill collections from **Microsoft** and **Google Gemini**. This update increases the total skill count to **845+**, making the library even more comprehensive for AI coding assistants. ✨ Key Changes 1. New Official Skills - **Microsoft Skills**: Added a massive collection of official skills from [microsoft/skills](https://github.com/microsoft/skills). - Includes Azure, .NET, Python, TypeScript, and Semantic Kernel skills. - Preserves the original directory structure under `skills/official/microsoft/`. - Includes plugin skills from the `.github/plugins` directory. - **Gemini Skills**: Added official Gemini API development skills under `skills/gemini-api-dev/`. 2. New Scripts & Tooling - **`scripts/sync_microsoft_skills.py`**: A robust synchronization script that: - Clones the official Microsoft repository. - Preserves the original directory heirarchy. - Handles symlinks and plugin locations. - Generates attribution metadata. - **`scripts/tests/inspect_microsoft_repo.py`**: Debug tool to inspect the remote repository structure. - **`scripts/tests/test_comprehensive_coverage.py`**: Verification script to ensure 100% of skills are captured during sync. 3. Core Improvements - **`scripts/generate_index.py`**: Enhanced frontmatter parsing to safely handle unquoted values containing `@` symbols and commas (fixing issues with some Microsoft skill descriptions). - **`package.json`**: Added `sync:microsoft` and `sync:all-official` scripts for easy maintenance. 4. Documentation - Updated `README.md` to reflect the new skill counts (845+) and added Microsoft/Gemini to the provider list. - Updated `CATALOG.md` and `skills_index.json` with the new skills. 🧪 Verification - Ran `scripts/tests/test_comprehensive_coverage.py` to verify all Microsoft skills are detected. - Validated `generate_index.py` fixes by successfully indexing the new skills.
This commit is contained in:
488
skills/official/microsoft/dotnet/messaging/eventgrid/SKILL.md
Normal file
488
skills/official/microsoft/dotnet/messaging/eventgrid/SKILL.md
Normal file
@@ -0,0 +1,488 @@
|
||||
---
|
||||
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://<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 |
|
||||
362
skills/official/microsoft/dotnet/messaging/eventhubs/SKILL.md
Normal file
362
skills/official/microsoft/dotnet/messaging/eventhubs/SKILL.md
Normal file
@@ -0,0 +1,362 @@
|
||||
---
|
||||
name: azure-eventhub-dotnet
|
||||
description: |
|
||||
Azure Event Hubs SDK for .NET. Use for high-throughput event streaming: sending events (EventHubProducerClient, EventHubBufferedProducerClient), receiving events (EventProcessorClient with checkpointing), partition management, and real-time data ingestion. Triggers: "Event Hubs", "event streaming", "EventHubProducerClient", "EventProcessorClient", "send events", "receive events", "checkpointing", "partition".
|
||||
package: Azure.Messaging.EventHubs
|
||||
---
|
||||
|
||||
# Azure.Messaging.EventHubs (.NET)
|
||||
|
||||
High-throughput event streaming SDK for sending and receiving events via Azure Event Hubs.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Core package (sending and simple receiving)
|
||||
dotnet add package Azure.Messaging.EventHubs
|
||||
|
||||
# Processor package (production receiving with checkpointing)
|
||||
dotnet add package Azure.Messaging.EventHubs.Processor
|
||||
|
||||
# Authentication
|
||||
dotnet add package Azure.Identity
|
||||
|
||||
# For checkpointing (required by EventProcessorClient)
|
||||
dotnet add package Azure.Storage.Blobs
|
||||
```
|
||||
|
||||
**Current Versions**: Azure.Messaging.EventHubs v5.12.2, Azure.Messaging.EventHubs.Processor v5.12.2
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
EVENTHUB_FULLY_QUALIFIED_NAMESPACE=<namespace>.servicebus.windows.net
|
||||
EVENTHUB_NAME=<event-hub-name>
|
||||
|
||||
# For checkpointing (EventProcessorClient)
|
||||
BLOB_STORAGE_CONNECTION_STRING=<storage-connection-string>
|
||||
BLOB_CONTAINER_NAME=<checkpoint-container>
|
||||
|
||||
# Alternative: Connection string auth (not recommended for production)
|
||||
EVENTHUB_CONNECTION_STRING=Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=...
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
// Always use DefaultAzureCredential for production
|
||||
var credential = new DefaultAzureCredential();
|
||||
|
||||
var fullyQualifiedNamespace = Environment.GetEnvironmentVariable("EVENTHUB_FULLY_QUALIFIED_NAMESPACE");
|
||||
var eventHubName = Environment.GetEnvironmentVariable("EVENTHUB_NAME");
|
||||
|
||||
var producer = new EventHubProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
credential);
|
||||
```
|
||||
|
||||
**Required RBAC Roles**:
|
||||
- **Sending**: `Azure Event Hubs Data Sender`
|
||||
- **Receiving**: `Azure Event Hubs Data Receiver`
|
||||
- **Both**: `Azure Event Hubs Data Owner`
|
||||
|
||||
## Client Types
|
||||
|
||||
| Client | Purpose | When to Use |
|
||||
|--------|---------|-------------|
|
||||
| `EventHubProducerClient` | Send events immediately in batches | Real-time sending, full control over batching |
|
||||
| `EventHubBufferedProducerClient` | Automatic batching with background sending | High-volume, fire-and-forget scenarios |
|
||||
| `EventHubConsumerClient` | Simple event reading | Prototyping only, NOT for production |
|
||||
| `EventProcessorClient` | Production event processing | **Always use this for receiving in production** |
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Send Events (Batch)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
await using var producer = new EventHubProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Create a batch (respects size limits automatically)
|
||||
using EventDataBatch batch = await producer.CreateBatchAsync();
|
||||
|
||||
// Add events to batch
|
||||
var events = new[]
|
||||
{
|
||||
new EventData(BinaryData.FromString("{\"id\": 1, \"message\": \"Hello\"}")),
|
||||
new EventData(BinaryData.FromString("{\"id\": 2, \"message\": \"World\"}"))
|
||||
};
|
||||
|
||||
foreach (var eventData in events)
|
||||
{
|
||||
if (!batch.TryAdd(eventData))
|
||||
{
|
||||
// Batch is full - send it and create a new one
|
||||
await producer.SendAsync(batch);
|
||||
batch = await producer.CreateBatchAsync();
|
||||
|
||||
if (!batch.TryAdd(eventData))
|
||||
{
|
||||
throw new Exception("Event too large for empty batch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send remaining events
|
||||
if (batch.Count > 0)
|
||||
{
|
||||
await producer.SendAsync(batch);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Send Events (Buffered - High Volume)
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
var options = new EventHubBufferedProducerClientOptions
|
||||
{
|
||||
MaximumWaitTime = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
|
||||
await using var producer = new EventHubBufferedProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential(),
|
||||
options);
|
||||
|
||||
// Handle send success/failure
|
||||
producer.SendEventBatchSucceededAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Batch sent: {args.EventBatch.Count} events");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
producer.SendEventBatchFailedAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Batch failed: {args.Exception.Message}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
// Enqueue events (sent automatically in background)
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
await producer.EnqueueEventAsync(new EventData($"Event {i}"));
|
||||
}
|
||||
|
||||
// Flush remaining events before disposing
|
||||
await producer.FlushAsync();
|
||||
```
|
||||
|
||||
### 3. Receive Events (Production - EventProcessorClient)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Consumer;
|
||||
using Azure.Messaging.EventHubs.Processor;
|
||||
using Azure.Storage.Blobs;
|
||||
|
||||
// Blob container for checkpointing
|
||||
var blobClient = new BlobContainerClient(
|
||||
Environment.GetEnvironmentVariable("BLOB_STORAGE_CONNECTION_STRING"),
|
||||
Environment.GetEnvironmentVariable("BLOB_CONTAINER_NAME"));
|
||||
|
||||
await blobClient.CreateIfNotExistsAsync();
|
||||
|
||||
// Create processor
|
||||
var processor = new EventProcessorClient(
|
||||
blobClient,
|
||||
EventHubConsumerClient.DefaultConsumerGroup,
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Handle events
|
||||
processor.ProcessEventAsync += async args =>
|
||||
{
|
||||
Console.WriteLine($"Partition: {args.Partition.PartitionId}");
|
||||
Console.WriteLine($"Data: {args.Data.EventBody}");
|
||||
|
||||
// Checkpoint after processing (or batch checkpoints)
|
||||
await args.UpdateCheckpointAsync();
|
||||
};
|
||||
|
||||
// Handle errors
|
||||
processor.ProcessErrorAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Error: {args.Exception.Message}");
|
||||
Console.WriteLine($"Partition: {args.PartitionId}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
// Start processing
|
||||
await processor.StartProcessingAsync();
|
||||
|
||||
// Run until cancelled
|
||||
await Task.Delay(Timeout.Infinite, cancellationToken);
|
||||
|
||||
// Stop gracefully
|
||||
await processor.StopProcessingAsync();
|
||||
```
|
||||
|
||||
### 4. Partition Operations
|
||||
|
||||
```csharp
|
||||
// Get partition IDs
|
||||
string[] partitionIds = await producer.GetPartitionIdsAsync();
|
||||
|
||||
// Send to specific partition (use sparingly)
|
||||
var options = new SendEventOptions
|
||||
{
|
||||
PartitionId = "0"
|
||||
};
|
||||
await producer.SendAsync(events, options);
|
||||
|
||||
// Use partition key (recommended for ordering)
|
||||
var batchOptions = new CreateBatchOptions
|
||||
{
|
||||
PartitionKey = "customer-123" // Events with same key go to same partition
|
||||
};
|
||||
using var batch = await producer.CreateBatchAsync(batchOptions);
|
||||
```
|
||||
|
||||
## EventPosition Options
|
||||
|
||||
Control where to start reading:
|
||||
|
||||
```csharp
|
||||
// Start from beginning
|
||||
EventPosition.Earliest
|
||||
|
||||
// Start from end (new events only)
|
||||
EventPosition.Latest
|
||||
|
||||
// Start from specific offset
|
||||
EventPosition.FromOffset(12345)
|
||||
|
||||
// Start from specific sequence number
|
||||
EventPosition.FromSequenceNumber(100)
|
||||
|
||||
// Start from specific time
|
||||
EventPosition.FromEnqueuedTime(DateTimeOffset.UtcNow.AddHours(-1))
|
||||
```
|
||||
|
||||
## ASP.NET Core Integration
|
||||
|
||||
```csharp
|
||||
// Program.cs
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
using Microsoft.Extensions.Azure;
|
||||
|
||||
builder.Services.AddAzureClients(clientBuilder =>
|
||||
{
|
||||
clientBuilder.AddEventHubProducerClient(
|
||||
builder.Configuration["EventHub:FullyQualifiedNamespace"],
|
||||
builder.Configuration["EventHub:Name"]);
|
||||
|
||||
clientBuilder.UseCredential(new DefaultAzureCredential());
|
||||
});
|
||||
|
||||
// Inject in controller/service
|
||||
public class EventService
|
||||
{
|
||||
private readonly EventHubProducerClient _producer;
|
||||
|
||||
public EventService(EventHubProducerClient producer)
|
||||
{
|
||||
_producer = producer;
|
||||
}
|
||||
|
||||
public async Task SendAsync(string message)
|
||||
{
|
||||
using var batch = await _producer.CreateBatchAsync();
|
||||
batch.TryAdd(new EventData(message));
|
||||
await _producer.SendAsync(batch);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `EventProcessorClient` for receiving** — Never use `EventHubConsumerClient` in production
|
||||
2. **Checkpoint strategically** — After N events or time interval, not every event
|
||||
3. **Use partition keys** — For ordering guarantees within a partition
|
||||
4. **Reuse clients** — Create once, use as singleton (thread-safe)
|
||||
5. **Use `await using`** — Ensures proper disposal
|
||||
6. **Handle `ProcessErrorAsync`** — Always register error handler
|
||||
7. **Batch events** — Use `CreateBatchAsync()` to respect size limits
|
||||
8. **Use buffered producer** — For high-volume scenarios with automatic batching
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging.EventHubs;
|
||||
|
||||
try
|
||||
{
|
||||
await producer.SendAsync(batch);
|
||||
}
|
||||
catch (EventHubsException ex) when (ex.Reason == EventHubsException.FailureReason.ServiceBusy)
|
||||
{
|
||||
// Retry with backoff
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
catch (EventHubsException ex) when (ex.IsTransient)
|
||||
{
|
||||
// Transient error - safe to retry
|
||||
Console.WriteLine($"Transient error: {ex.Message}");
|
||||
}
|
||||
catch (EventHubsException ex)
|
||||
{
|
||||
// Non-transient error
|
||||
Console.WriteLine($"Error: {ex.Reason} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Checkpointing Strategies
|
||||
|
||||
| Strategy | When to Use |
|
||||
|----------|-------------|
|
||||
| Every event | Low volume, critical data |
|
||||
| Every N events | Balanced throughput/reliability |
|
||||
| Time-based | Consistent checkpoint intervals |
|
||||
| Batch completion | After processing a logical batch |
|
||||
|
||||
```csharp
|
||||
// Checkpoint every 100 events
|
||||
private int _eventCount = 0;
|
||||
|
||||
processor.ProcessEventAsync += async args =>
|
||||
{
|
||||
// Process event...
|
||||
|
||||
_eventCount++;
|
||||
if (_eventCount >= 100)
|
||||
{
|
||||
await args.UpdateCheckpointAsync();
|
||||
_eventCount = 0;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Messaging.EventHubs` | Core sending/receiving | `dotnet add package Azure.Messaging.EventHubs` |
|
||||
| `Azure.Messaging.EventHubs.Processor` | Production processing | `dotnet add package Azure.Messaging.EventHubs.Processor` |
|
||||
| `Azure.ResourceManager.EventHubs` | Management plane (create hubs) | `dotnet add package Azure.ResourceManager.EventHubs` |
|
||||
| `Microsoft.Azure.WebJobs.Extensions.EventHubs` | Azure Functions binding | `dotnet add package Microsoft.Azure.WebJobs.Extensions.EventHubs` |
|
||||
333
skills/official/microsoft/dotnet/messaging/servicebus/SKILL.md
Normal file
333
skills/official/microsoft/dotnet/messaging/servicebus/SKILL.md
Normal file
@@ -0,0 +1,333 @@
|
||||
---
|
||||
name: azure-servicebus-dotnet
|
||||
description: |
|
||||
Azure Service Bus SDK for .NET. Enterprise messaging with queues, topics, subscriptions, and sessions. Use for reliable message delivery, pub/sub patterns, dead letter handling, and background processing. Triggers: "Service Bus", "ServiceBusClient", "ServiceBusSender", "ServiceBusReceiver", "ServiceBusProcessor", "message queue", "pub/sub .NET", "dead letter queue".
|
||||
package: Azure.Messaging.ServiceBus
|
||||
---
|
||||
|
||||
# Azure.Messaging.ServiceBus (.NET)
|
||||
|
||||
Enterprise messaging SDK for reliable message delivery with queues, topics, subscriptions, and sessions.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.Messaging.ServiceBus
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v7.20.1 (stable)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SERVICEBUS_FULLY_QUALIFIED_NAMESPACE=<namespace>.servicebus.windows.net
|
||||
# Or connection string (less secure)
|
||||
AZURE_SERVICEBUS_CONNECTION_STRING=Endpoint=sb://...
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Microsoft Entra ID (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.ServiceBus;
|
||||
|
||||
string fullyQualifiedNamespace = "<namespace>.servicebus.windows.net";
|
||||
await using ServiceBusClient client = new(fullyQualifiedNamespace, new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### Connection String
|
||||
|
||||
```csharp
|
||||
string connectionString = "<connection_string>";
|
||||
await using ServiceBusClient client = new(connectionString);
|
||||
```
|
||||
|
||||
### ASP.NET Core Dependency Injection
|
||||
|
||||
```csharp
|
||||
services.AddAzureClients(builder =>
|
||||
{
|
||||
builder.AddServiceBusClientWithNamespace("<namespace>.servicebus.windows.net");
|
||||
builder.UseCredential(new DefaultAzureCredential());
|
||||
});
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
ServiceBusClient
|
||||
├── CreateSender(queueOrTopicName) → ServiceBusSender
|
||||
├── CreateReceiver(queueName) → ServiceBusReceiver
|
||||
├── CreateReceiver(topicName, subName) → ServiceBusReceiver
|
||||
├── AcceptNextSessionAsync(queueName) → ServiceBusSessionReceiver
|
||||
├── CreateProcessor(queueName) → ServiceBusProcessor
|
||||
└── CreateSessionProcessor(queueName) → ServiceBusSessionProcessor
|
||||
|
||||
ServiceBusAdministrationClient (separate client for CRUD)
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Send Messages
|
||||
|
||||
```csharp
|
||||
await using ServiceBusClient client = new(fullyQualifiedNamespace, new DefaultAzureCredential());
|
||||
ServiceBusSender sender = client.CreateSender("my-queue");
|
||||
|
||||
// Single message
|
||||
ServiceBusMessage message = new("Hello world!");
|
||||
await sender.SendMessageAsync(message);
|
||||
|
||||
// Safe batching (recommended)
|
||||
using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();
|
||||
if (batch.TryAddMessage(new ServiceBusMessage("Message 1")))
|
||||
{
|
||||
// Message added successfully
|
||||
}
|
||||
if (batch.TryAddMessage(new ServiceBusMessage("Message 2")))
|
||||
{
|
||||
// Message added successfully
|
||||
}
|
||||
await sender.SendMessagesAsync(batch);
|
||||
```
|
||||
|
||||
### 2. Receive Messages
|
||||
|
||||
```csharp
|
||||
ServiceBusReceiver receiver = client.CreateReceiver("my-queue");
|
||||
|
||||
// Single message
|
||||
ServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();
|
||||
string body = message.Body.ToString();
|
||||
Console.WriteLine(body);
|
||||
|
||||
// Complete the message (removes from queue)
|
||||
await receiver.CompleteMessageAsync(message);
|
||||
|
||||
// Batch receive
|
||||
IReadOnlyList<ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync(maxMessages: 10);
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
Console.WriteLine(msg.Body.ToString());
|
||||
await receiver.CompleteMessageAsync(msg);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Message Settlement
|
||||
|
||||
```csharp
|
||||
// Complete - removes message from queue
|
||||
await receiver.CompleteMessageAsync(message);
|
||||
|
||||
// Abandon - releases lock, message can be received again
|
||||
await receiver.AbandonMessageAsync(message);
|
||||
|
||||
// Defer - prevents normal receive, use ReceiveDeferredMessageAsync
|
||||
await receiver.DeferMessageAsync(message);
|
||||
|
||||
// Dead Letter - moves to dead letter subqueue
|
||||
await receiver.DeadLetterMessageAsync(message, "InvalidFormat", "Message body was not valid JSON");
|
||||
```
|
||||
|
||||
### 4. Background Processing with Processor
|
||||
|
||||
```csharp
|
||||
ServiceBusProcessor processor = client.CreateProcessor("my-queue", new ServiceBusProcessorOptions
|
||||
{
|
||||
AutoCompleteMessages = false,
|
||||
MaxConcurrentCalls = 2
|
||||
});
|
||||
|
||||
processor.ProcessMessageAsync += async (args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string body = args.Message.Body.ToString();
|
||||
Console.WriteLine($"Received: {body}");
|
||||
await args.CompleteMessageAsync(args.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error processing: {ex.Message}");
|
||||
await args.AbandonMessageAsync(args.Message);
|
||||
}
|
||||
};
|
||||
|
||||
processor.ProcessErrorAsync += (args) =>
|
||||
{
|
||||
Console.WriteLine($"Error source: {args.ErrorSource}");
|
||||
Console.WriteLine($"Entity: {args.EntityPath}");
|
||||
Console.WriteLine($"Exception: {args.Exception}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await processor.StartProcessingAsync();
|
||||
// ... application runs
|
||||
await processor.StopProcessingAsync();
|
||||
```
|
||||
|
||||
### 5. Sessions (Ordered Processing)
|
||||
|
||||
```csharp
|
||||
// Send session message
|
||||
ServiceBusMessage message = new("Hello")
|
||||
{
|
||||
SessionId = "order-123"
|
||||
};
|
||||
await sender.SendMessageAsync(message);
|
||||
|
||||
// Receive from next available session
|
||||
ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync("my-queue");
|
||||
|
||||
// Or receive from specific session
|
||||
ServiceBusSessionReceiver receiver = await client.AcceptSessionAsync("my-queue", "order-123");
|
||||
|
||||
// Session state management
|
||||
await receiver.SetSessionStateAsync(new BinaryData("processing"));
|
||||
BinaryData state = await receiver.GetSessionStateAsync();
|
||||
|
||||
// Renew session lock
|
||||
await receiver.RenewSessionLockAsync();
|
||||
```
|
||||
|
||||
### 6. Dead Letter Queue
|
||||
|
||||
```csharp
|
||||
// Receive from dead letter queue
|
||||
ServiceBusReceiver dlqReceiver = client.CreateReceiver("my-queue", new ServiceBusReceiverOptions
|
||||
{
|
||||
SubQueue = SubQueue.DeadLetter
|
||||
});
|
||||
|
||||
ServiceBusReceivedMessage dlqMessage = await dlqReceiver.ReceiveMessageAsync();
|
||||
|
||||
// Access dead letter metadata
|
||||
string reason = dlqMessage.DeadLetterReason;
|
||||
string description = dlqMessage.DeadLetterErrorDescription;
|
||||
Console.WriteLine($"Dead letter reason: {reason} - {description}");
|
||||
```
|
||||
|
||||
### 7. Topics and Subscriptions
|
||||
|
||||
```csharp
|
||||
// Send to topic
|
||||
ServiceBusSender topicSender = client.CreateSender("my-topic");
|
||||
await topicSender.SendMessageAsync(new ServiceBusMessage("Broadcast message"));
|
||||
|
||||
// Receive from subscription
|
||||
ServiceBusReceiver subReceiver = client.CreateReceiver("my-topic", "my-subscription");
|
||||
var message = await subReceiver.ReceiveMessageAsync();
|
||||
```
|
||||
|
||||
### 8. Administration (CRUD)
|
||||
|
||||
```csharp
|
||||
var adminClient = new ServiceBusAdministrationClient(
|
||||
fullyQualifiedNamespace,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Create queue
|
||||
var options = new CreateQueueOptions("my-queue")
|
||||
{
|
||||
MaxDeliveryCount = 10,
|
||||
LockDuration = TimeSpan.FromSeconds(30),
|
||||
RequiresSession = true,
|
||||
DeadLetteringOnMessageExpiration = true
|
||||
};
|
||||
QueueProperties queue = await adminClient.CreateQueueAsync(options);
|
||||
|
||||
// Update queue
|
||||
queue.LockDuration = TimeSpan.FromSeconds(60);
|
||||
await adminClient.UpdateQueueAsync(queue);
|
||||
|
||||
// Create topic and subscription
|
||||
await adminClient.CreateTopicAsync(new CreateTopicOptions("my-topic"));
|
||||
await adminClient.CreateSubscriptionAsync(new CreateSubscriptionOptions("my-topic", "my-subscription"));
|
||||
|
||||
// Delete
|
||||
await adminClient.DeleteQueueAsync("my-queue");
|
||||
```
|
||||
|
||||
### 9. Cross-Entity Transactions
|
||||
|
||||
```csharp
|
||||
var options = new ServiceBusClientOptions { EnableCrossEntityTransactions = true };
|
||||
await using var client = new ServiceBusClient(connectionString, options);
|
||||
|
||||
ServiceBusReceiver receiverA = client.CreateReceiver("queueA");
|
||||
ServiceBusSender senderB = client.CreateSender("queueB");
|
||||
|
||||
ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync();
|
||||
|
||||
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
|
||||
{
|
||||
await receiverA.CompleteMessageAsync(receivedMessage);
|
||||
await senderB.SendMessageAsync(new ServiceBusMessage("Forwarded"));
|
||||
ts.Complete();
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ServiceBusClient` | Main entry point, manages connection |
|
||||
| `ServiceBusSender` | Sends messages to queues/topics |
|
||||
| `ServiceBusReceiver` | Receives messages from queues/subscriptions |
|
||||
| `ServiceBusSessionReceiver` | Receives session messages |
|
||||
| `ServiceBusProcessor` | Background message processing |
|
||||
| `ServiceBusSessionProcessor` | Background session processing |
|
||||
| `ServiceBusAdministrationClient` | CRUD for queues/topics/subscriptions |
|
||||
| `ServiceBusMessage` | Message to send |
|
||||
| `ServiceBusReceivedMessage` | Received message with metadata |
|
||||
| `ServiceBusMessageBatch` | Batch of messages |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use singletons** — Clients, senders, receivers, and processors are thread-safe
|
||||
2. **Always dispose** — Use `await using` or call `DisposeAsync()`
|
||||
3. **Dispose order** — Close senders/receivers/processors first, then client
|
||||
4. **Use DefaultAzureCredential** — Prefer over connection strings for production
|
||||
5. **Use processors for background work** — Handles lock renewal automatically
|
||||
6. **Use safe batching** — `CreateMessageBatchAsync()` and `TryAddMessage()`
|
||||
7. **Handle transient errors** — Use `ServiceBusException.Reason`
|
||||
8. **Configure transport** — Use `AmqpWebSockets` if ports 5671/5672 are blocked
|
||||
9. **Set appropriate lock duration** — Default is 30 seconds
|
||||
10. **Use sessions for ordering** — FIFO within a session
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
await sender.SendMessageAsync(message);
|
||||
}
|
||||
catch (ServiceBusException ex) when (ex.Reason == ServiceBusFailureReason.ServiceBusy)
|
||||
{
|
||||
// Retry with backoff
|
||||
}
|
||||
catch (ServiceBusException ex)
|
||||
{
|
||||
Console.WriteLine($"Service Bus Error: {ex.Reason} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Messaging.ServiceBus` | Service Bus (this SDK) | `dotnet add package Azure.Messaging.ServiceBus` |
|
||||
| `Azure.Messaging.EventHubs` | Event streaming | `dotnet add package Azure.Messaging.EventHubs` |
|
||||
| `Azure.Messaging.EventGrid` | Event routing | `dotnet add package Azure.Messaging.EventGrid` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.Messaging.ServiceBus |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.messaging.servicebus |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/servicebus/Azure.Messaging.ServiceBus |
|
||||
| Troubleshooting | https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/TROUBLESHOOTING.md |
|
||||
Reference in New Issue
Block a user