private BaseCacheManager(string name, ICacheManagerConfiguration configuration)
        {
            NotNullOrWhiteSpace(name, nameof(name));
            NotNull(configuration, nameof(configuration));

            Name          = name;
            Configuration = configuration;

            var loggerFactory = CacheReflectionHelper.CreateLoggerFactory(configuration);
            var serializer    = CacheReflectionHelper.CreateSerializer(configuration, loggerFactory);

            Logger = loggerFactory.CreateLogger(this);

            _logTrace = Logger.IsEnabled(LogLevel.Trace);

            Logger.LogInfo("Cache manager: adding cache handles...");

            try
            {
                _cacheHandles = CacheReflectionHelper.CreateCacheHandles(this, loggerFactory, serializer).ToArray();

                var index = 0;
                foreach (var handle in _cacheHandles)
                {
                    var handleIndex = index;
                    handle.OnCacheSpecificRemove += (sender, args) =>
                    {
                        // base cache handle does logging for this
                        TriggerOnRemoveByHandle(args.Key, args.Region, args.Reason, handleIndex + 1, args.Value);
                        if (Configuration.UpdateMode == CacheUpdateMode.Up)
                        {
                            if (_logTrace)
                            {
                                Logger.LogTrace("Cleaning handles above '{0}' because of remove event.", handleIndex);
                            }

                            EvictFromHandlesAbove(args.Key, args.Region, handleIndex);
                        }
                    };

                    index++;
                }

                _cacheBackplane = CacheReflectionHelper.CreateBackplane(configuration, loggerFactory);
                if (_cacheBackplane != null)
                {
                    RegisterCacheBackplane(_cacheBackplane);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error occurred while creating the cache manager.");
                throw ex.InnerException ?? ex;
            }
        }
        private void RegisterCacheBackplane(CacheBackplane backplane)
        {
            NotNull(backplane, nameof(backplane));

            // this should have been checked during activation already, just to be totally sure...
            if (_cacheHandles.Any(p => p.Configuration.IsBackplaneSource))
            {
                // added includeSource param to get the handles which need to be synced.
                // in case the backplane source is non-distributed (in-memory), only remotly triggered remove and clear should also
                // trigger a sync locally. For distribtued caches, we expect that the distributed cache is already the source and in sync
                // as that's the layer which triggered the event. In this case, only other in-memory handles above the distribtued, would be synced.
                var handles = new Func <bool, BaseCacheHandle <TCacheValue>[]>((includSource) =>
                {
                    var handleList = new List <BaseCacheHandle <TCacheValue> >();
                    foreach (var handle in _cacheHandles)
                    {
                        if (!handle.Configuration.IsBackplaneSource ||
                            (includSource && handle.Configuration.IsBackplaneSource && !handle.IsDistributedCache))
                        {
                            handleList.Add(handle);
                        }
                    }
                    return(handleList.ToArray());
                });

                backplane.Changed += (sender, args) =>
                {
                    if (Logger.IsEnabled(LogLevel.Debug))
                    {
                        Logger.LogDebug("Backplane event: [Changed] for '{1}:{0}'.", args.Key, args.Region);
                    }

                    EvictFromHandles(args.Key, args.Region, handles(false));
                    switch (args.Action)
                    {
                    case CacheItemChangedEventAction.Add:
                        TriggerOnAdd(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;

                    case CacheItemChangedEventAction.Put:
                        TriggerOnPut(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;

                    case CacheItemChangedEventAction.Update:
                        TriggerOnUpdate(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;
                    }
                };

                backplane.Removed += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Remove] of {0} {1}.", args.Key, args.Region);
                    }

                    EvictFromHandles(args.Key, args.Region, handles(true));
                    TriggerOnRemove(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                };

                backplane.Cleared += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Clear].");
                    }

                    ClearHandles(handles(true));
                    TriggerOnClear(CacheActionEventArgOrigin.Remote);
                };

                backplane.ClearedRegion += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Clear Region] region: {0}.", args.Region);
                    }

                    ClearRegionHandles(args.Region, handles(true));
                    TriggerOnClearRegion(args.Region, CacheActionEventArgOrigin.Remote);
                };
            }
        }
        private BaseCacheManager(string name, ICacheManagerConfiguration configuration)
        {
            NotNullOrWhiteSpace(name, nameof(name));
            NotNull(configuration, nameof(configuration));

            Name          = name;
            Configuration = configuration;

            var loggerFactory = CacheReflectionHelper.CreateLoggerFactory(configuration);
            var serializer    = CacheReflectionHelper.CreateSerializer(configuration, loggerFactory);

            Logger = loggerFactory.CreateLogger(this);

            _logTrace = Logger.IsEnabled(LogLevel.Trace);

            Logger.LogInfo("Cache manager: adding cache handles...");

            try
            {
                _cacheHandles = CacheReflectionHelper.CreateCacheHandles(this, loggerFactory, serializer).ToArray();

                var index = 0;
                foreach (var handle in _cacheHandles)
                {
                    var handleIndex = index;
                    handle.OnCacheSpecificRemove += (sender, args) =>
                    {
                        // added sync for using backplane with in-memory caches on cache specific removal
                        // but commented for now, this is not really needed if all instances use the same expiration etc, would just cause dublicated events
                        ////if (_cacheBackplane != null && handle.Configuration.IsBackplaneSource && !handle.IsDistributedCache)
                        ////{
                        ////    if (string.IsNullOrEmpty(args.Region))
                        ////    {
                        ////        _cacheBackplane.NotifyRemove(args.Key);
                        ////    }
                        ////    else
                        ////    {
                        ////        _cacheBackplane.NotifyRemove(args.Key, args.Region);
                        ////    }
                        ////}

                        // base cache handle does logging for this

                        if (Configuration.UpdateMode == CacheUpdateMode.Up)
                        {
                            if (_logTrace)
                            {
                                Logger.LogTrace("Cleaning handles above '{0}' because of remove event.", handleIndex);
                            }

                            EvictFromHandlesAbove(args.Key, args.Region, handleIndex);
                        }

                        // moving down below cleanup, optherwise the item could still be in memory
                        TriggerOnRemoveByHandle(args.Key, args.Region, args.Reason, handleIndex + 1, args.Value);
                    };

                    index++;
                }

                _cacheBackplane = CacheReflectionHelper.CreateBackplane(configuration, loggerFactory);
                if (_cacheBackplane != null)
                {
                    RegisterCacheBackplane(_cacheBackplane);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error occurred while creating the cache manager.");
                throw ex.InnerException ?? ex;
            }
        }
        private void RegisterCacheBackplane(CacheBackplane backplane)
        {
            NotNull(backplane, nameof(backplane));

            // this should have been checked during activation already, just to be totally sure...
            if (_cacheHandles.Any(p => p.Configuration.IsBackplaneSource))
            {
                var handles = new Func <BaseCacheHandle <TCacheValue>[]>(() =>
                {
                    var handleList = new List <BaseCacheHandle <TCacheValue> >();
                    foreach (var handle in _cacheHandles)
                    {
                        if (!handle.Configuration.IsBackplaneSource)
                        {
                            handleList.Add(handle);
                        }
                    }
                    return(handleList.ToArray());
                });

                backplane.Changed += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Changed] of {0} {1}.", args.Key, args.Region);
                    }

                    EvictFromHandles(args.Key, args.Region, handles());
                    switch (args.Action)
                    {
                    case CacheItemChangedEventAction.Add:
                        TriggerOnAdd(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;

                    case CacheItemChangedEventAction.Put:
                        TriggerOnPut(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;

                    case CacheItemChangedEventAction.Update:
                        TriggerOnUpdate(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                        break;
                    }
                };

                backplane.Removed += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Remove] of {0} {1}.", args.Key, args.Region);
                    }

                    EvictFromHandles(args.Key, args.Region, handles());
                    TriggerOnRemove(args.Key, args.Region, CacheActionEventArgOrigin.Remote);
                };

                backplane.Cleared += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Clear].");
                    }

                    ClearHandles(handles());
                    TriggerOnClear(CacheActionEventArgOrigin.Remote);
                };

                backplane.ClearedRegion += (sender, args) =>
                {
                    if (_logTrace)
                    {
                        Logger.LogTrace("Backplane event: [Clear Region] region: {0}.", args.Region);
                    }

                    ClearRegionHandles(args.Region, handles());
                    TriggerOnClearRegion(args.Region, CacheActionEventArgOrigin.Remote);
                };
            }
        }