Skip to main content

RSCG_idempotency by Ignat Andrei

NuGet / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: RSCG_idempotency

Automatic generating Idempotency for function parameters

Author: Ignat Andrei

NuGet: https://www.nuget.org/packages/RSCG_idempotency/

You can find more details at https://github.com/ignatandrei/RSCG_idempotency

Source: https://github.com/ignatandrei/RSCG_idempotency

Author

note

Ignat Andrei Alt text

Original Readme

note

RSCG Idempotency Generator - Usage Guide

Installation

Install the package RSCG_Idempotency NuGet from nuget .

Overview

The RSCG (Roslyn Source Code Generator) Idempotency module provides automatic duplicate detection and prevention for method parameters. It uses the [Idempotent] attribute to mark parameters that should be tracked, generating helper infrastructure to ensure idempotent behavior.

Key Concepts

What is Idempotency?

Idempotency means that performing the same operation multiple times with identical inputs produces the same result as performing it once. This generator helps enforce this by:

  • Tracking which parameter values have been processed before
  • Providing methods to check for duplicates
  • Cleaning up old cached entries automatically

Generated Components

  1. IdempotentAttribute - Marks parameters for tracking
  2. TimeProvider - Supplies UTC timestamps for cache entries
  3. Helper Methods - Generated per method to manage the duplicate tracking

Usage

Step 1: Mark Parameters with [Idempotent]

In your C# method, apply the [Idempotent] attribute to parameters you want to track:

using RSCG_IdemPotency;

public partial class YourClass
{
public void ProcessData([Idempotent] string input, int retry)
{
// Your implementation here
// The generator will create tracking infrastructure for 'input'
}
}

Step 2: Generated Helper Methods

For each method with [Idempotent] parameters, the generator creates:

Check for Duplicates

private bool MethodName_ExistsBefore(T parameter)
{
// Returns true if the parameter was processed before
// Returns false if it's the first occurrence
}

Example:

public void ProcessData([Idempotent] string input, int retry)
{
if (ProcessData_ExistsBefore(input))
{
// This input was already processed
Console.WriteLine("Duplicate detected, skipping");
return;
}

// Process new input
Console.WriteLine($"Processing: {input}");
}

Clean Up Old Entries

private void MethodName_DeletePreviousData(DateTimeOffset dt)
{
// Removes cached entries older than the specified date
}

Example:

public void CleanupCache()
{
var oneHourAgo = DateTimeOffset.UtcNow.AddHours(-1);
ProcessData_DeletePreviousData(oneHourAgo);
Console.WriteLine("Cleaned up entries older than 1 hour");
}

Generated Infrastructure Details

Caching Mechanism

For each method, a static ConcurrentDictionary is created:

static ConcurrentDictionary<ParameterType, DateTimeOffset> MethodName_Dict = [];
  • Key: The parameter value being tracked (e.g., string, int)
  • Value: UTC timestamp when first encountered

Thread Safety

The generator uses ConcurrentDictionary to ensure thread-safe operations in multi-threaded scenarios.

Complete Example

Original Method Definition

using RSCG_IdemPotency;

public partial class PaymentProcessor
{
public void ProcessPayment([Idempotent] string transactionId, int maxRetries)
{
if (ProcessPayment_ExistsBefore(transactionId))
{
Console.WriteLine($"Transaction {transactionId} already processed");
return;
}

// Process new transaction
Console.WriteLine($"Processing payment for transaction: {transactionId}");
// ... payment logic ...
}

public void MaintenanceCleanup()
{
// Remove transactions processed more than 30 days ago
var thirtyDaysAgo = DateTimeOffset.UtcNow.AddDays(-30);
ProcessPayment_DeletePreviousData(thirtyDaysAgo);
}
}

Generated Code (Auto-created by RSCG)

partial class PaymentProcessor
{
static ConcurrentDictionary<string, DateTimeOffset> ProcessPayment_Dict = [];

private bool ProcessPayment_ExistsBefore(string transactionId)
{
var tp = IdemPotentTimeProvider.timeProv;
var utcDate = tp.GetUtcNow();
var existing = ProcessPayment_Dict.GetOrAdd(transactionId, utcDate);
if (utcDate == existing) return false; // First time
return true; // Seen before
}

private void ProcessPayment_DeletePreviousData(DateTimeOffset dt)
{
var values = ProcessPayment_Dict.Where(it => it.Value < dt).ToArray();
if (values.Length == 0) return;
foreach (var item in values)
{
ProcessPayment_Dict.TryRemove(item.Key, out _);
}
}
}

Best Practices

1. Use Partial Classes

The generated code modifies partial classes, so declare your class as partial:

public partial class MyService
{
public void MyMethod([Idempotent] string input, int retry)
{
// Implementation
}
}

2. Check for Duplicates Early

Always call the *_ExistsBefore() method at the start of your logic:

public void ProcessRequest([Idempotent] string requestId)
{
if (ProcessRequest_ExistsBefore(requestId))
return; // Skip duplicate

// Main logic
}

3. Implement Cache Cleanup

For long-running applications, periodically clean old entries:

public void PeriodicMaintenance()
{
var oneDayAgo = DateTimeOffset.UtcNow.AddDays(-1);
MyMethod_DeletePreviousData(oneDayAgo);
}

4. Multiple Parameters

If multiple parameters need tracking, apply [Idempotent] to each:

public void Transfer([Idempotent] string sourceAccount, [Idempotent] string destAccount, decimal amount)
{
if (Transfer_ExistsBefore(sourceAccount) || Transfer_ExistsBefore(destAccount))
return;
// Process transfer
}

5. Memory Considerations

The tracking dictionary persists for the lifetime of the application. For high-volume scenarios:

  • Implement aggressive cleanup schedules
  • Consider application restart strategies
  • Monitor cache size in production

Time Provider

The generator uses IdemPotentTimeProvider.timeProv (set to TimeProvider.System by default) for UTC timestamps. This enables:

  • Accurate timestamp recording
  • Testability (can be mocked in tests)
  • System-independent time handling

Summary

ComponentPurpose
[Idempotent] attributeMarks parameters for duplicate tracking
*_ExistsBefore() methodChecks if a parameter was previously processed
*_DeletePreviousData() methodCleans up old cache entries
*_Dict static dictionaryStores tracked parameter values and timestamps
IdemPotentTimeProviderSupplies UTC timestamps

This generator simplifies idempotent operation implementation, reducing the boilerplate needed for duplicate detection and prevention.

About

note

Generating code for idempotency

How to use

Example (source csproj, source files)

This is the CSharp Project that references RSCG_idempotency

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.2" />
<PackageReference Include="RSCG_idempotency" Version="10.2026.127.2000" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX


//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool. Name: https://github.com/ignatandrei/RSCG_idempotency
// Runtime Version: 2026-01-27T18:12:56.0000000
// ( name : Ivo Andric is feeling cut in Vientiane )
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
//</auto-generated>
//------------------------------------------------------------------------------
using System.Linq;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;
namespace IdempotencyDemo {
partial class Purchase {


static ConcurrentDictionary<string , DateTimeOffset> PurchaseNow_Dict=[];
/// <summary>
/// Verify if the argument was before
///</summary>
///<param name="input">the data</param>
/// <returns>true if exists before;false if encountered first time</returns>
[global::System.CodeDom.Compiler.GeneratedCode("RSCG_idempotency", "2026.10127.11812")]
private bool PurchaseNow_ExistsBefore(
string UniqueId) {

var tp=IdemPotentTimeProvider.timeProv;
var utcDate = tp.GetUtcNow();
var existing = PurchaseNow_Dict.GetOrAdd(UniqueId,utcDate);
if(utcDate == existing) return false;//it was inserted now
return true;

}
/// <summary>
/// clear the data before a date
///</summary>
///<param name="dt">date to remove previous</param>
private void PurchaseNow_DeletePreviousData(DateTimeOffset dt)
{
var values = PurchaseNow_Dict.Where(it => it.Value < dt).ToArray();
if (values.Length == 0) return;
foreach (var item in values)
{
PurchaseNow_Dict.TryRemove(item.Key,out _);
}
}

}

}

Useful

Download Example (.NET C#)

Share RSCG_idempotency

https://ignatandrei.github.io/RSCG_Examples/v2/docs/RSCG_idempotency

Category "Idempotency" has the following generators:

1 RSCG_idempotency Nuget GitHub Repo stars 2026-01-28

See category

Idempotency