/// <summary>
        /// Sets the serializer which should be used to serialize cache items.
        /// </summary>
        /// <param name="serializerType">The type of the serializer.</param>
        /// <param name="args">The optional arguments to activate the serializer.</param>
        /// <returns>The builder part.</returns>
        public ConfigurationBuilderCachePart WithSerializer(Type serializerType, params object[] args)
        {
            var instance = CacheReflectionHelper.CreateSerializer(serializerType, args);

            this.Configuration.WithSerializer(instance);
            return(this);
        }
        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 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;
            }
        }
        internal static CacheManagerConfiguration LoadFromSection(CacheManagerSection section, string configName)
        {
            NotNullOrWhiteSpace(configName, nameof(configName));

            var handleDefsSection = section.CacheHandleDefinitions;

            Ensure(handleDefsSection.Count > 0, "There are no cache handles defined.");

            // load handle definitions as lookup
            var handleDefs = new SortedList <string, CacheHandleConfiguration>();

            foreach (CacheHandleDefinition def in handleDefsSection)
            {
                //// don't validate at this point, otherwise we will get an exception if any defined handle doesn't match with the requested type...
                //// CacheReflectionHelper.ValidateCacheHandleGenericTypeArguments(def.HandleType, cacheValue);

                var normId = def.Id.ToUpper(CultureInfo.InvariantCulture);
                handleDefs.Add(
                    normId,
                    new CacheHandleConfiguration(def.Id)
                {
                    HandleType        = def.HandleType,
                    ExpirationMode    = def.DefaultExpirationMode,
                    ExpirationTimeout = GetTimeSpan(def.DefaultTimeout, "defaultTimeout")
                });
            }

            // retrieve the handles collection with the correct name
            CacheManagerHandleCollection managerCfg = section.CacheManagers.FirstOrDefault(p => p.Name.Equals(configName, StringComparison.OrdinalIgnoreCase));

            EnsureNotNull(managerCfg, "No cache manager configuration found for name [{0}]", configName);

            int?maxRetries = managerCfg.MaximumRetries;

            if (maxRetries.HasValue && maxRetries.Value <= 0)
            {
                throw new InvalidOperationException("Maximum number of retries must be greater than zero.");
            }

            int?retryTimeout = managerCfg.RetryTimeout;

            if (retryTimeout.HasValue && retryTimeout.Value < 0)
            {
                throw new InvalidOperationException("Retry timeout must be greater than or equal to zero.");
            }

            // build configuration
            var cfg = new CacheManagerConfiguration(managerCfg.UpdateMode, maxRetries.HasValue ? maxRetries.Value : int.MaxValue, retryTimeout.HasValue ? retryTimeout.Value : 10);

            if (string.IsNullOrWhiteSpace(managerCfg.BackPlateType))
            {
                if (!string.IsNullOrWhiteSpace(managerCfg.BackPlateName))
                {
                    throw new InvalidOperationException("BackPlateType cannot be null if BackPlateName is specified.");
                }
            }
            else
            {
                if (string.IsNullOrWhiteSpace(managerCfg.BackPlateName))
                {
                    throw new InvalidOperationException("BackPlateName cannot be null if BackPlateType is specified.");
                }

                cfg.WithBackPlate(
                    Type.GetType(managerCfg.BackPlateType, true),
                    managerCfg.BackPlateName);
            }

            // build serializer if set
            if (!string.IsNullOrWhiteSpace(managerCfg.SerializerType))
            {
                var serializerType = Type.GetType(managerCfg.SerializerType, false);
                EnsureNotNull(serializerType, "Serializer type cannot be loaded, {0}", managerCfg.SerializerType);

                cfg.WithSerializer(CacheReflectionHelper.CreateSerializer(serializerType));
            }

            foreach (CacheManagerHandle handleItem in managerCfg)
            {
                var normRefId = handleItem.RefHandleId.ToUpper(CultureInfo.InvariantCulture);

                Ensure(
                    handleDefs.ContainsKey(normRefId),
                    "Referenced cache handle [{0}] cannot be found in cache handles definition.",
                    handleItem.RefHandleId);

                var handleDef = handleDefs[normRefId];

                var handle = new CacheHandleConfiguration(handleItem.Name)
                {
                    HandleType                = handleDef.HandleType,
                    ExpirationMode            = handleDef.ExpirationMode,
                    ExpirationTimeout         = handleDef.ExpirationTimeout,
                    EnableStatistics          = managerCfg.EnableStatistics,
                    EnablePerformanceCounters = managerCfg.EnablePerformanceCounters,
                    IsBackPlateSource         = handleItem.IsBackPlateSource
                };

                // override default timeout if it is defined in this section.
                if (!string.IsNullOrWhiteSpace(handleItem.Timeout))
                {
                    handle.ExpirationTimeout = GetTimeSpan(handleItem.Timeout, "timeout");
                }

                // override default expiration mode if it is defined in this section.
                if (!string.IsNullOrWhiteSpace(handleItem.ExpirationMode))
                {
                    try
                    {
                        handle.ExpirationMode = (ExpirationMode)Enum.Parse(typeof(ExpirationMode), handleItem.ExpirationMode);
                    }
                    catch (ArgumentException ex)
                    {
                        throw new InvalidOperationException("Invalid value '" + handleItem.ExpirationMode + "'for expiration mode", ex);
                    }
                }

                if (handle.ExpirationMode != ExpirationMode.None && handle.ExpirationTimeout == TimeSpan.Zero)
                {
                    throw new InvalidOperationException(
                              string.Format(
                                  CultureInfo.InvariantCulture,
                                  "Expiration mode set without a valid timeout specified for handle [{0}]",
                                  handle.HandleName));
                }

                cfg.CacheHandleConfigurations.Add(handle);
            }

            Ensure(cfg.CacheHandleConfigurations.Count > 0, "There are no valid cache handles linked to the cache manager configuration [{0}]", configName);

            return(cfg);
        }