Skip to content

Посты

Реализация кэширования запросов в ASP.Net Core

8 июля 2019 г. • 4 мин чтения

Реализация кэширования запросов в ASP.Net Core

В какой-то момент разработки приложения, обычно довольно рано, вы понимаете, что приложение работает медленно. После некоторого исследования виновником оказывается ненужное извлечение одних и тех же данных, и загорается лампочка, и вы думаете: “Мне нужно кэширование.”

Кэширование - это бесценный паттерн для устранения избыточных вызовов к базе данных или стороннему API. Microsoft предоставляет IMemoryCache для временного кэширования, однако иногда временное кэширование - это не то, что вам нужно. В этой статье мы рассмотрим кэширование в области запроса и то, как оно может нам помочь.

Что такое кэширование запросов? Кэширование запросов - это механизм кэширования данных на время жизни веб-запроса. В dot-net у нас была эта возможность в некоторой степени с коллекцией HttpContext.Items, однако HttpContext не известен своей инъектируемостью.

Кэширование в области запроса имеет несколько преимуществ: Во-первых, оно устраняет проблему устаревших данных. В большинстве сценариев запрос выполняется менее чем за секунду, что обычно недостаточно долго для того, чтобы данные устарели. И во-вторых, истечение срока действия не является проблемой, потому что данные умирают, когда запрос заканчивается.

Из коробки Asp.Net Core не имеет инъектируемого кэширования. Как упоминалось ранее, HttpContext.Items является опцией, но это не элегантное решение.

К счастью для нас, ASP.Net Core дает нам инструменты для создания инъектируемой реализации кэширования запросов, используя встроенный фреймворк внедрения зависимостей (DI).

Встроенный DI фреймворк имеет три времени жизни для зависимостей: Singleton, Scoped и Transient. Singleton - на время жизни приложения, Scoped - на время жизни запроса, а Transient - новый экземпляр с каждым запросом.

Я создал интерфейс, смоделированный по интерфейсу IMemoryCache, чтобы сохранить согласованность.

Интерфейс

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);
}

Реализация

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);
    }
}

Используя DI фреймворк ASP.Net Core, мы подключим его как Scoped.

services.AddScoped<IRequestCache, RequestCache>();

Использование

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}";
    }
}

Вот и все! Кэширование запросов теперь инъектируется в любом месте, где оно вам нужно.

Посетите Git-репозиторий и не стесняйтесь опробовать код.

Автор: Чак Конвей специализируется на разработке программного обеспечения и генеративном ИИ. Свяжитесь с ним в социальных сетях: X (@chuckconway) или посетите его на YouTube.

↑ Наверх

Вам также может понравиться