Аннулирование кэшированных данных и шаблона внедрения зависимостей - PullRequest
1 голос
/ 26 сентября 2019

У меня есть класс кэша данных (который использует класс MemoryCache).

Основная функция этого класса - кэшировать справочные данные.Чтобы получить эти справочные данные, необходим экземпляр Entity Framework dbContext.Это передается путем внедрения зависимостей (Simple Injector).

Но этот dbContext имеет жизненный цикл "на вызов" (AsyncScopedLifestyle).Поэтому, чтобы удовлетворить это, я поместил вызов для настройки кэша в «область действия», срок действия которой истекает после вызова.

Кэш теряет силу каждые 2 часа и запрашивается повторно.Неудивительно, что к тому времени dbContext был очищен (потому что он вышел за рамки).

Я могу придумать способы обойти эту проблему.Но я хочу знать, есть ли образец, которому я должен следовать для такого рода проблемы.(В большинстве моих решений я передаю контейнер в класс кеша. Но это, кажется, нарушение нескольких шаблонов DI.)

Кто-нибудь знает шаблон проектирования, который можно использовать при повторном появлениинужно для внедрения внутри класса?

Немного больше информации:

  • Мой класс кэша (называемый DataCache) получает контекст от внедрения конструктора.
  • Вызов, чтобы установить это, сделан из метода Configure в Startup.cs.Это выглядит следующим образом:

.

using (AsyncScopedLifestyle.BeginScope(container))
{
    // Setup the long lived data caching
    var dataCache = container.GetInstance<DataCache>();
    dataCache.SetupCachedItems();
}
  • Устанавливает в MemoryCache срок действия данных в кеше через два часа.Но внедренный контекст уже давно очищен.

Ответы [ 2 ]

2 голосов
/ 26 сентября 2019

Я вижу два общих решения:

  1. Переместите кеш, которым DataCache управляет из этого класса, таким образом, что MyCacheClass может стать Scoped.Это кажется легким делом, поскольку это, вероятно, то, для чего MemoryCache.Кэш памяти, скорее всего, является Singleton.
  2. Переместите DataCache в Корень композиции , чтобы он мог безопасно зависеть от контейнера (или абстракции контейнера), не попадая в Service Locatoranti-pattern trap.

Первое решение может быть применено несколькими способами.Возможно, это вопрос определения кэша в поле static:

public class DataCache
{
    private static ConcurrentDictionary<string, object> cache;
}

И в случае, если вы введете MemoryCache в качестве поставщика хранилища для ваших данных, он будет содержать кэш и образ жизни DataCache становится неактуальным:

public class DataCache
{
    public DataCache(MyContext context, IMemoryCache cache)
}

Если, однако, DataCache необходимо вводить потребителям синглтона, он сам должен быть синглтоном.Это запрещает этот подход, так как MyContext необходимо определить, чтобы предотвратить зависимых зависимостей .Для этого вы можете использовать решение 2.

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

// Part of the Composition Root
sealed class DataCache: IDataCache
{
    public DataCache(Container container, IMemoryCache cache) ...

    public ProductData GetProductByKey(string key)
    {
        if (key not in cache)
        {
            using (AsyncScopedLifestyle.BeginScope(this.container))
            {
                var context = container.GetInstance<MyContext>();
                var p = context.Products.SingleOrDefault(p => p.Key == key);
                var data = new ProductData(p);
                AddProductToCache(key, data);
                return data;
            }
        }
    }
}
0 голосов
/ 26 сентября 2019

Вы должны полностью полагаться на ДИ.Другими словами, если класс кеша нуждается в контексте, то это зависимость, и ее следует внедрить следующим образом:

public class MyCacheClass
{
    private readonly MyContext _context;

    public MyCacheClass(MyContext context)
    {
        _context = context;
    }

    ...
}

Это, конечно, предполагает, что у класса кеша также есть время жизни с ограничениями, что на самом деленет причин, по которым это не следует делать, так как он взаимодействует с зависимостями в рамках.Однако, если по какой-то причине вам нужно, чтобы он имел одноэтапное время жизни, вы можете просто ввести IServiceProvider, а затем создать область и вытащить контекст, когда вам это нужно:

using (var scope = _serviceProvider.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<MyContext>();
    // do something with context
}

Если вы 'Вы используете статический класс, не надо.

...