/// <summary>
        /// Load the configuration data from the PubComp CacheConfig ConfigurationSource
        /// and return it as an ordered list of relevant ConfigNode types
        /// e.g.:
        /// "PubComp:CacheConfig:NoCache1:Assembly"->"PubComp.Caching.Core",
        /// "PubComp:CacheConfig:NoCache1:Type"->"NoCache",
        /// "PubComp:CacheConfig:YesCache2:Assembly"->"PubComp.Caching.Core",
        /// "PubComp:CacheConfig:YesCache2:Type"->"JustAnExampleCache",
        /// ...
        /// </summary>
        /// <returns>The list of Cache ConfigNode (and its inheriting classes)</returns>
        public IList <ConfigNode> LoadConfig()
        {
            var loadErrors   = new CacheConfigLoadErrorsException();
            var assemblies   = new Dictionary <string, Assembly>();
            var configNodes  = new List <ConfigNode>();
            var cacheConfigs = pubCompCacheConfigurationSection.GetChildren();

            foreach (var cacheConfig in cacheConfigs)
            {
                if (cacheConfig.GetValue <ConfigAction>("action") == ConfigAction.Remove)
                {
                    configNodes.Add(new RemoveConfig {
                        Action = ConfigAction.Remove, Name = cacheConfig.Key
                    });
                    continue;
                }
                var typeName     = cacheConfig["type"] + "Config";
                var assemblyName = cacheConfig["assembly"];

                if (!assemblies.TryGetValue(assemblyName, out var assembly))
                {
                    try
                    {
                        assembly = Assembly.Load(assemblyName);
                    }
                    catch (Exception ex) when(ex is FileLoadException ||
                                              ex is FileNotFoundException ||
                                              ex is BadImageFormatException)
                    {
                        LogConfigError(loadErrors, $"Could not load assembly {assemblyName} for type {typeName}", ex);
                        continue;
                    }

                    assemblies.Add(assemblyName, assembly);
                }

                var configType = assembly.GetType(typeName, false, false);
                if (configType == null)
                {
                    configType = assembly.GetType(assemblyName + '.' + typeName, false, false);
                }

                if (configType == null)
                {
                    LogConfigError(loadErrors, $"Could not load type {typeName} from assembly {assemblyName}");
                    continue;
                }

                var configNode = cacheConfig.Get(configType) as ConfigNode;
                configNode.Name = cacheConfig["Name"] ?? cacheConfig.Key; // Use explicit name if possible. Key is the fallback name.
                configNodes.Add(configNode);
            }

            if (!loadErrors.IsEmpty())
            {
                throw loadErrors;
            }

            return(configNodes);
        }
        // TODO: Use real log here - and throw a fatal exception instead of swallowing the missing assembly/type
        private void LogConfigError(
            CacheConfigLoadErrorsException loadErrors, string error, Exception ex = null)
        {
            System.Diagnostics.Debug.WriteLine(
                ex != null
                    ? $"{typeof(MicrosoftExtensionsCacheConfigLoader).FullName}: {error}, Exception: {ex}"
                    : $"{typeof(MicrosoftExtensionsCacheConfigLoader).FullName}: {error}");

            loadErrors.Add(
                new CacheConfigLoadErrorsException.CacheConfigLoadError
            {
                Error     = error,
                Exception = ex
            });
        }