# Idempotency

The `IdempotentRequest` attribute is designed to ensure that a certain action is executed only once, regardless of the number of requests received. This is particularly useful for preventing duplicate processing in situations where the client might send the same request multiple times, intentionally or unintentionally.

#### Installation

You should install **Eiffel.CrossCutting.Idempotency** NuGet package into your application.&#x20;

```sh
dotnet add package Eiffel.CrossCutting.Idempotency
```

This feature requires distributed caching, pelase visit [Caching](/features/caching.md) module.

#### Usage

To use this attribute, simply annotate the desired action method in your controller with

```csharp
[IdempotentRequest]
public async Task PostAsync([FromBody] CreateBookingRequest request)
{
  .. // Your implementation details
}
```

#### Options

```csharp
/// <summary>
/// Idempotency options
/// </summary>
public class IdempotencyOptions
{
    /// <summary>
    /// Header key identifier
    /// </summary>
    public string HeaderKeyIdentifier { get; set; } = "x-idempotency-key";

    /// <summary>
    /// Expire time
    /// </summary>
    public int ExpireTimeInSeconds { get; set; } = 0;

    /// <summary>
    /// Cache key prefix
    /// </summary>
    public string CacheKeyPrefix { get; set; } = "idempotency:{0}";

    /// <summary>
    /// Cache db index
    /// </summary>
    public int CacheDbIndex { get; set; } = 0;
}
```

The `IdempotencyOptions` class provides configurable options for the idempotency module. These options allow you to specify the behavior of idempotency checks in the system.\
\
\&#xNAN;*HeaderKeyIdentifier***:** Specifies the name of the HTTP header used to uniquely identify a request for idempotency checks.\
\
\&#xNAN;*ExpireTimeInSeconds***:** Sets the duration for which an idempotency record is retained in the cache. After this duration, the record is automatically removed, allowing a request with the same idempotency key to be processed again. The time is expressed in seconds. A value of `0` indicates that the records do not expire automatically.\
\
\&#xNAN;*CacheKeyPrefix***:** Defines the format of the cache keys used to store idempotency records. The `{0}` placeholder is replaced with the actual idempotency key from the request.\
\
\&#xNAN;*CacheDbIndex***:** Specifies the database index used when storing idempotency records in a Redis cache (or similar distributed cache that supports multiple databases). This allows you to separate idempotency records from other data in your cache.<br>

**Configuration**\
This class is typically used in the Startup configuration method where it is registered with the dependency injection system and its properties are set based on configuration file values or hardcoded settings. After being registered, the options are used by the idempotency middleware or action filter to determine the behavior of idempotency checks.

{% hint style="info" %}
The `IdempotencyOptions` configuration is optional; if not specified, the system will use the default values.
{% endhint %}

```csharp
// Program.cs (.NET5 and above)
public static async Task Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.Configure<IdempotencyOptions>(options =>
    {
        options.HeaderKeyIdentifier = "X-Request-Id";
        options.ExpireTimeInSeconds = 3600;
        options.CacheKeyPrefix = "customPrefix:{0}";
        options.CacheDbIndex = 1;
    });
 
    var app = builder.Build();
    app.Run();
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eiffel.dev/features/cross-cutting-concerns/idempotency.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
