TGViewer
C# Geeks (.NET) C# Geeks (.NET) @csharpgeeks · 549 subscribers
Post #326 198
🔒 ملاحظات حرفه‌ای: شرط رقابت (Race Condition)


یک پنجره کوچک شرط رقابت (Race Condition) بین بررسی کش و تنظیم آن وجود دارد. برای سازگاری مطلق، باید الگوی قفل توزیع شده (Distributed Lock) را در نظر بگیریم، اگرچه این کار باعث افزایش پیچیدگی و تأخیر (Latency) خواهد شد. ⚠️

🚀 اعمال Attribute به اکشن‌های کنترلر
اکنون، می‌توانیم این Attribute را به اکشن‌های کنترلر خود اعمال کنیم:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
[HttpPost]
[Idempotent(cacheTimeInMinutes: 60)]
public IActionResult CreateOrder([FromBody] CreateOrderRequest request)
{
// Process the order...

return CreatedAtAction(nameof(GetOrder), new { id = orderDto.Id }, orderDto);
}
}


✨ هم‌توانی با Minimal APIs

برای پیاده‌سازی هم‌توانی با Minimal APIs، می‌توانیم از IEndpointFilter استفاده کنیم. 🛠

📜 کد: IdempotencyFilter
internal sealed class IdempotencyFilter(int cacheTimeInMinutes = 60)
: IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
// Parse the Idempotence-Key header from the request
if (TryGetIdempotenceKey(out Guid idempotenceKey))
{
return Results.BadRequest("Invalid or missing Idempotence-Key header");
}

IDistributedCache cache = context.HttpContext
.RequestServices.GetRequiredService<IDistributedCache>();

// Check if we already processed this request and return a cached response (if it exists)
string cacheKey = $"Idempotent_{idempotenceKey}";
string? cachedResult = await cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
IdempotentResponse response = JsonSerializer.Deserialize<IdempotentResponse>(cachedResult)!;
return new IdempotentResult(response.StatusCode, response.Value);
}

object? result = await next(context);

// Execute the request and cache the response for the specified duration
if (result is IStatusCodeHttpResult { StatusCode: >= 200 and < 300 } statusCodeResult
and IValueHttpResult valueResult)
{
int statusCode = statusCodeResult.StatusCode ?? StatusCodes.Status200OK;
IdempotentResponse response = new(statusCode, valueResult.Value);

await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(response),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(cacheTimeInMinutes)
}
);
}

return result;
}
}

// We have to implement a custom result to write the status code
internal sealed class IdempotentResult : IResult
{
private readonly int _statusCode;
private readonly object? _value;

public IdempotentResult(int statusCode, object? value)
{
_statusCode = statusCode;
_value = value;
}

public Task ExecuteAsync(HttpContext httpContext)
{
httpContext.Response.StatusCode = _statusCode;

return httpContext.Response.WriteAsJsonAsync(_value);
}
}


🔗 اعمال فیلتر به اِندپوینت

اکنون، می‌توانیم این فیلتر اِندپوینت را به اِندپوینت Minimal API خود اعمال کنیم:
app.MapPost("/api/orders", CreateOrder)
.RequireAuthorization()
.WithOpenApi()
.AddEndpointFilter<IdempotencyFilter>();


💡 جایگزین دیگر

یک جایگزین برای دو پیاده‌سازی قبلی (Attribute و Endpoint Filter) پیاده‌سازی منطق هم‌توانی در یک Middleware سفارشی است. 🌐
More from @csharpgeeks
  1. Sep 22, 2026یه مدتی قراره از دنیای NET. فاصله بگیرم، چون وقتشه برم سربازی. راستش نمیدونم این مدت رو چج…
  2. Sep 20, 2026🔥 حالا مشکل اصلی: Alert Storm فرض کن Database از دسترس خارج شده. ۱۰۰ Pod داری. هر Pod می‌…
  3. Sep 20, 2026🚨 طراحی سیستم Monitoring و Alerting در یک سیستم بزرگ فرض کن ساعت ۳ صبح است. سیستم شما با…
  4. Sep 19, 2026#Engineering_Leadership تصمیم نگرفتن هم یک تصمیم است یه چیز عجیب توی تیم‌های مهندسی: گاهی…
  5. Sep 19, 2026☑ چک‌لیست آماده‌سازی تیم، فرایندها و زیرساخت برای توسعه با AI توجه: هیچ چک‌لیستی جهان‌شمول…
  6. Sep 19, 2026📌پایان یک انتظار طولانی: اعتبارسنجی ناهمگام (Async Validation) در NET 11.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →