Zarządzanie stanem asynchronicznym

📦 Microsoft.Extensions.AsyncState Pakiet NuGet udostępnia funkcje przechowywania i pobierania obiektów w bieżącym kontekście asynchronicznym. Ten pakiet oferuje ulepszenia wydajności i użyteczności w przypadku bezpośredniego używania AsyncLocal<T> , szczególnie w przypadku konieczności współużytkowania wielu obiektów w operacjach asynchronicznych.

Dlaczego warto używać klasy AsyncState

Chociaż .NET zapewnia AsyncLocal<T> do zarządzania danymi otoczenia w kontekstach asynchronicznych, użycie ich bezpośrednio może mieć wady:

  • Wydajność: każde AsyncLocal<T> wystąpienie dodaje obciążenie. Jeśli wiele obiektów musi przepływać przez konteksty asynchroniczne, zarządzanie wieloma AsyncLocal<T> wystąpieniami może mieć wpływ na wydajność.
  • pl-PL: Abstrakcja: bezpośrednie użycie AsyncLocal<T> wiąże twój kod z konkretną implementacją, co utrudnia optymalizację lub zmianę w przyszłości.
  • Zarządzanie okresem istnienia: pakiet AsyncState zapewnia lepszą kontrolę nad okresem istnienia danych otoczenia za pośrednictwem jawnych interfejsów API.

Pakiet Microsoft.Extensions.AsyncState rozwiązuje te problemy, udostępniając następujące kwestie:

  • Zoptymalizowana implementacja, która zmniejsza liczbę potrzebnych AsyncLocal<T> wystąpień.
  • Czysta abstrakcja do przechowywania i pobierania danych otoczenia.
  • Integracja z iniekcją zależności w celu ułatwienia testowania i konfigurowania.

Zacznij

Aby zacząć z zarządzaniem stanem asynchronicznym, zainstaluj pakiet NuGet Microsoft.Extensions.AsyncState.

dotnet add package Microsoft.Extensions.AsyncState

Aby uzyskać więcej informacji, zobacz dotnet add package lub Zarządzanie zależnościami pakietów w aplikacjach .NET.

Rejestrowanie usług stanu asynchronicznego

Zarejestruj usługi stanu asynchronicznego w kontenerze wstrzykiwania zależności przy użyciu metody rozszerzenia AddAsyncState:

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddAsyncState();

ServiceProvider provider = services.BuildServiceProvider();

Ta rejestracja udostępnia interfejsy IAsyncContext<T> i IAsyncState do wstrzykiwania zależności.

Korzystanie z elementu IAsyncContext

Interfejs IAsyncContext<T> udostępnia metody do pobierania i ustawiania wartości w bieżącym kontekście asynchronicznym.

var context = provider.GetRequiredService<IAsyncContext<UserContext>>();

// Set a value in the async context
var userContext = new UserContext { UserId = "12345", UserName = "Alice" };
context.Set(userContext);

// Retrieve the value
if (context.TryGet(out var retrievedContext))
{
    Console.WriteLine($"User: {retrievedContext.UserName}");
}
public record class UserContext
{
    public required string UserId { get; set; }
    public required string UserName { get; set; }
}

Wartość ustawiona w kontekście przepływa przez operacje asynchroniczne, udostępniając go wszystkim kodom wykonywanym w tym samym kontekście asynchronicznym.

Użycie IAsyncState

Interfejs IAsyncState to podstawowy interfejs cyklu życia, który IAsyncContext<T> rozszerza. Initialize() i Reset() metody zapewnia do zarządzania cyklem życia stanu asynchronicznego. Aby uzyskać typizowany dostęp do wartości stanu asynchronicznego, użyj IAsyncContext<T>.

Praktyczny przykład: korelacja żądań

Typowy przypadek użycia stanu asynchronicznego to utrzymywanie informacji o korelacji w obrębie żądania HTTP.

public class RequestProcessor(IAsyncContext<CorrelationContext> asyncContext)
{

    public async Task ProcessRequestAsync(string correlationId)
    {
        // Set correlation context at the beginning of request processing
        asyncContext.Set(new CorrelationContext { CorrelationId = correlationId });

        // The correlation ID flows through all async operations
        await Step1Async();
        await Step2Async();
    }

    private async Task Step1Async()
    {
        await Task.Yield();
        
        if (asyncContext.TryGet(out var context))
        {
            Console.WriteLine($"Step 1 - Correlation ID: {context.CorrelationId}");
        }
    }

    private async Task Step2Async()
    {
        await Task.Yield();
        
        if (asyncContext.TryGet(out var context))
        {
            Console.WriteLine($"Step 2 - Correlation ID: {context.CorrelationId}");
        }
    }
}
public class CorrelationContext
{
    public required string CorrelationId { get; set; }
}

W tym przykładzie identyfikator korelacji jest ustawiany raz na początku przetwarzania żądań i jest automatycznie dostępny we wszystkich kolejnych operacjach asynchronicznych bez konieczności przekazywania go jako parametru.

integracja ASP.NET Core

W aplikacjach ASP.NET Core możesz użyć stanu asynchronicznego do przepływu informacji specyficznych dla żądania za pośrednictwem aplikacji:

public class RequestHandler(IAsyncContext<RequestMetadata> asyncContext)
{

    public async Task<object> GetDataAsync()
    {
        await Task.Yield();
        
        if (asyncContext.TryGet(out var metadata))
        {
            var duration = DateTimeOffset.UtcNow - metadata.StartTime;
            return new
            {
                RequestId = metadata.RequestId,
                RequestPath = metadata.RequestPath,
                Duration = duration.TotalMilliseconds
            };
        }

        return new { Error = "No request metadata available" };
    }
}
public record class RequestMetadata
{
    public required string RequestId { get; set; }
    public required string RequestPath { get; set; }
    public DateTimeOffset StartTime { get; set; }
}

Najlepsze rozwiązania

W przypadku korzystania ze stanu asynchronicznego należy wziąć pod uwagę następujące najlepsze rozwiązania:

  • Rozmiar stanu limitu: zachowaj małe obiekty stanu asynchronicznego, aby zmniejszyć obciążenie pamięci i utrzymać wydajność.
  • Wczesne inicjowanie stanu: Ustaw wartości stanu asynchronicznego tak szybko, jak to możliwe, aby upewnić się, że są dostępne dla wszystkich późniejszych operacji asynchronicznych.
  • Stan czyszczenia: zresetuj lub wyczyść stan asynchroniczny, gdy nie jest już potrzebny, aby uniknąć przecieków pamięci w długotrwałych aplikacjach.
  • Użyj odpowiednich interfejsów: użyj IAsyncContext<T> , aby pobrać i ustawić typizowane wartości w bieżącym kontekście asynchronicznym. Użyj IAsyncState, gdy potrzebujesz bezpośrednio zarządzać cyklem życia inicjalizacji i resetowania.
  • Type safety: Tworzenie określonych typów kontekstowych zamiast używania słowników ogólnych w celu zachowania bezpieczeństwa typów i ulepszania clarity kodu.

Zobacz także