Course contentsShow
← C#
Lesson 1584 of 1,90814. Background Work and MessagingFree lesson

Designing for Idempotency Keys

Implement and validate business-level idempotency keys to ensure consistent outcome processing.

In this lesson, you will learn how to design robust business-level idempotency keys to ensure that processing the same message multiple times results in exactly one state change.

The Core Concept: Semantic Idempotency

When background workers process messages, network jitters or service crashes often lead to "at-least-once" delivery. If your worker blindly processes every incoming message, you risk duplicate transactions or corrupted state. An idempotency key is a unique identifier—generated by the producer—that acts as a "fingerprint" for a specific business operation.

Instead of checking if a record exists by its ID, we treat the idempotency key as the source of truth for the intent of the request.

Implementation Strategy

The most effective way to implement this in C# is by using an atomic "check-and-set" pattern within your persistence layer. By leveraging a dedicated IdempotencyTable, you ensure that the business logic and the record of the processed key are committed within the same transaction.

public async Task ProcessOrderAsync(OrderRequest request, string idempotencyKey)
{
    using var transaction = await _dbContext.Database.BeginTransactionAsync();
    
    // Attempt to insert the idempotency key; fail if it already exists
    var processed = await _dbContext.IdempotencyLogs
        .AddAsync(new IdempotencyLog { Key = idempotencyKey, ProcessedAt = DateTime.UtcNow });

    try 
    {
        await _dbContext.SaveChangesAsync(); // Will throw if key constraint is violated
        
        // Execute core business logic
        await _orderService.PlaceOrder(request);
        
        await transaction.CommitAsync();
    }
    catch (DbUpdateException) 
    {
        // Key already processed: skip logic, return cached result or success
        _logger.LogInformation("Duplicate request ignored: {Key}", idempotencyKey);
    }
}

Why Business-Level Keys?

Using the idempotency key at the business level—rather than just the message ID—allows the system to handle retries gracefully. If the PlaceOrder operation succeeds but the acknowledgment message fails, the system will receive the same key again. Because our IdempotencyLog enforces a UNIQUE constraint at the database level, the second attempt will be safely ignored, preventing a double charge or duplicate order fulfillment. This approach ensures your system remains predictable, even when faced with the non-deterministic nature of distributed messaging environments.

Key Takeaway: Always pair your business operations with a unique idempotency key inside a single database transaction to guarantee that duplicate messages never result in duplicate side effects.