Skip to content

Innlegg

Implementering av forespørselscaching i ASP.Net Core

8. juli 2019 • 4 min lesing

Implementering av forespørselscaching i ASP.Net Core

På et eller annet punkt i en applikasjons utvikling, vanligvis ganske tidlig, innser du at applikasjonen er treg. Etter litt forskning viser det seg at problemet er unødvendig henting av de samme dataene, og det går opp et lys, og du tenker: “Jeg trenger litt caching.”

Caching er et uvurderlig mønster for å eliminere redundante kall til en database eller et tredjepartsAPI. Microsoft tilbyr IMemoryCache for tidsbasert caching, men noen ganger er tidsbasert caching ikke det du trenger. I denne artikkelen ser vi på Request Scoped caching og hvordan det kan være til nytte for oss.

Hva er Request caching? Request caching er en mekanisme for å cache data for varigheten av en webforespørsel. I dot-net har vi hatt denne muligheten i noen form med HttpContext.Items-samlingen, men HttpContext er ikke kjent for sin injiserbarhet.

Request Scoped caching har noen fordeler: For det første eliminerer det bekymringen for foreldede data. I de fleste scenarioer kjøres en forespørsel på mindre enn ett sekund, noe som vanligvis ikke er lenge nok til at data blir foreldede. For det andre er utløp ikke et problem fordi dataene dør når forespørselen avsluttes.

Ut av boksen har Asp.Net Core ikke injiserbar caching. Som nevnt tidligere er HttpContext.Items et alternativ, men det er ikke en elegant løsning.

Heldigvis gir ASP.Net Core oss verktøyene til å lage en injiserbar Request Caching-implementering ved å bruke det innebygde avhengighetsinjeksjonsrammeverket (DI).

Det innebygde DI-rammeverket har tre levetider for avhengigheter: Singleton, Scoped og Transient. Singleton er for applikasjonens levetid, Scoped er for forespørselens levetid, og Transient er en ny instans med hver forespørsel.

Jeg har opprettet et grensesnitt modellert etter IMemoryCache-grensesnittet for å holde ting konsistent.

Grensesnitt

public interface IRequestCache
{
    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    void Add<TValue>(string key, TValue value);

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    void Remove(string key);

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add);

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue Retrieve<TValue>(string key);
}

Implementering

public class RequestCache : IRequestCache
{
    IDictionary<string, object> _cache = new Dictionary<string, object>();

    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    public void Add<TValue>(string key, TValue value)
    {
        _cache[key] = value;
    }

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    public void Remove(string key)
    {
        if (_cache.ContainsKey(key))
        {
            _cache.Remove(key);
        }
    }

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        var value = add();

        _cache[key] = value;

        return value;
    }

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue Retrieve<TValue>(string key)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        return default(TValue);
    }
}

Ved å bruke ASP.Net Cores DI-rammeverk vil vi konfigurere det som Scoped.

services.AddScoped<IRequestCache, RequestCache>();

Bruk

public class UserService
{
    private readonly IRequestCache _cache;
    private readonly IUserRepository _userRepository;

    public UserService(IRequestCache cache, IUserRepository userRepository)
    {
        _cache = cache;
        _userRepository = userRepository;
    }

    public User RetrieveUserById(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        return _cache.RetrieveOrAdd(BuildCacheKey, () => { return _userRepository.RetrieveUserBy(userId); });
    }

    public void Delete(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        _userRepository.Delete(userId);
        _cache.Remove(BuildCacheKey(userId));
    }

    private static string BuildCacheKey(int userId)
    {
        return $"user_{userId}";
    }
}

Det er det! Request Caching er nå injiserbar hvor som helst du trenger det.

Besøk Git-repositoriet og ta gjerne koden for en test.

Forfatter: Chuck Conway er en AI-ingeniør med nesten 30 års erfaring innen programvareutvikling. Han bygger praktiske AI-systemer—innholdspipelines, infrastrukturagenter og verktøy som løser virkelige problemer—og deler det han lærer underveis. Koble til ham på sosiale medier: X (@chuckconway) eller besøk ham på YouTube og på SubStack.

↑ Tilbake til toppen

Du kan også like