Реализация центрального класса "менеджер" для экземпляров определенного типа в нескольких сборках

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

Поэтому я создал общий класс 'Cache', который является частным вложенным классом статического класса CacheManager. Из-за закрытого вложенного класса Cache этот класс не может быть инициирован никаким другим классом, кроме самого CacheManager.

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

Интерфейс ICache используется для предоставления кэшей коду вне класса CacheManager.

Упрощенные реализации:

Интерфейс

// Interface for all available caches
    public interface ICache<TKey, TValue>
    {
        bool            StatisticsEnabled   { get; set; }
        KeyStatistics[] Statistics          { get; }

        // Set cache item (including update)
        void Set(TKey key, TValue item);

        // Get an item from the cache (return value indicates found / not found in cache)
        bool Get(TKey key, out TValue value);
    }

Объекты данных для статистики

    // Statistics per key in the cache (or requested from the cache)
    public class KeyStatistics
    {
        public string Name { get; set; }
        public int RequestCount { get; set; }
        public int HitCount { get; set; }
    }

    // Contains statistics per cache
    public class CacheStatistics
    {
        public string           Name        { get; set; }
        public KeyStatistics[]  Statistics  { get; set; } 
    }

Статический менеджер кеша, включая реализацию общего кеша

    public static class CacheManager 
    {
        private static Lazy<Cache<string, string>> _settingsCache = new Lazy<Cache<string, string>>();
        private static Lazy<Cache<DateTime, short>> _mechanicCountCache = new Lazy<Cache<DateTime, short>>();

        // actual supported caches
        public static ICache<string, string> SettingsCache { get { return _settingsCache.Value; } }
        public static ICache<DateTime, short> MechanicCountCache { get { return _mechanicCountCache.Value; } }

        public static IEnumerable<CacheStatistics> Statistics
        {
            get 
            {
                yield return new CacheStatistics
                {
                    Name        = "Settings cache",
                    Statistics  = _settingsCache.Value.Statistics
                };
                yield return new CacheStatistics
                {
                    Name        = "Mechanics count cache",
                    Statistics = _mechanicCountCache.Value.Statistics
                };
            }
        }

        // Private class, so it cannot be initiated by anything else than the cache manager
        private class Cache<TKey, TValue> : ICache<TKey, TValue>
        {
            private ConcurrentDictionary<TKey, KeyStatistics> _statistics = new ConcurrentDictionary<TKey, KeyStatistics>();
            private ConcurrentDictionary<TKey, TValue> _cachedValues = new ConcurrentDictionary<TKey, TValue>();

            // Constructor
            public Cache() 
            {
                // Do some constructing things  
            }

            #region ICache
            public bool StatisticsEnabled { get; set; }

            public KeyStatistics[] Statistics
            {
                get { return _statistics.Values.ToArray(); }    
            }

            public void Set(TKey key, TValue item) 
            {
                // Todo: add to cache or update existing value

            }

            public bool Get(TKey key, out TValue item) 
            {
                // Todo: fetch from dictionary
                // Todo: update statistics

                item = default(TValue);
                return false;
            }

            #endregion ICache
        }

Это будет работать как требуется. Однако есть кое-что, что я еще не понял. Мы работаем с несколькими сборками (базовыми сборками и "специализированными" сборками). Специализированные сборки имеют ссылку на основные сборки, но не наоборот.

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

Есть ли способ добиться того, чего я хочу?

2 ответа

Решение

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

В реализации GetCache менеджер кэша использует универсальный класс Singleton, который может создавать одноэлементный экземпляр типа (в данном случае тип, реализующий ICache).

Перевод: * GetStatistieken() > GetStatistics() * Wissen() > Очистить все кэшированное содержимое

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

var cache = CacheManager.GetCache<DagCapaciteitCache>();

Для простоты создания нового кэша экземпляры кэша могут быть подклассами одного из двух базовых классов кэша, но это не обязательно. Единственное требование состоит в том, чтобы кеш реализовывал интерфейс IQuery (определенный в базовой сборке).

Немного поздно для OP. Для других реализация фабричного паттерна может быть выходом. Затем отражение используется для реализации всех типов на заводе и повторяется для сброса всех / определенных кешей.

например https://www.dofactory.com/net/factory-method-design-pattern

Надеюсь это поможет.

Вы можете хранить только сериализованные значения объектов в самом менеджере кэша. Ссылочные сборки должны иметь соответствующие специализированные ссылки на сборки.

В прошлом Json.NET хорошо работал для сериализации / десериализации универсальных объектов.

Другие вопросы по тегам