/// <summary>
        /// Adds a custom application store by a custom implementation derived
        /// from <see cref="IOpenIddictApplicationStore{TApplication}"/>.
        /// Note: when using this overload, the application store can be
        /// either a non-generic, a closed or an open generic service.
        /// </summary>
        /// <param name="type">The type of the custom store.</param>
        /// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
        public OpenIddictCoreBuilder AddApplicationStore([NotNull] Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore <>));

            if (root == null)
            {
                throw new ArgumentException("The specified type is invalid.", nameof(type));
            }

            // Note: managers can be either open generics (e.g OpenIddictApplicationStore<>)
            // or closed generics (e.g OpenIddictApplicationStore<OpenIddictApplication>).
            if (type.IsGenericTypeDefinition)
            {
                if (type.GetGenericArguments().Length != 1)
                {
                    throw new ArgumentException("The specified type is invalid.", nameof(type));
                }

                Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictApplicationStore <>), type));
            }

            else
            {
                Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictApplicationStore <>)
                                                          .MakeGenericType(root.GenericTypeArguments[0]), type));
            }

            return(this);
        }
        /// <summary>
        /// Returns a token store compatible with the specified token type or throws an
        /// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
        /// </summary>
        /// <typeparam name="TToken">The type of the Token entity.</typeparam>
        /// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
        public IOpenIddictTokenStore <TToken> Get <TToken>() where TToken : class
        {
            var store = _provider.GetService <IOpenIddictTokenStore <TToken> >();

            if (store != null)
            {
                return(store);
            }

            var type = _cache.GetOrAdd(typeof(TToken), key =>
            {
                var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken <, ,>));
                if (root == null)
                {
                    throw new InvalidOperationException(new StringBuilder()
                                                        .AppendLine("The specified token type is not compatible with the Entity Framework 6.x stores.")
                                                        .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in generic ")
                                                        .Append("'OpenIddictToken' entity (from the 'OpenIddict.Models' package) or a custom entity ")
                                                        .Append("that inherits from the generic 'OpenIddictToken' entity.")
                                                        .ToString());
                }

                return(typeof(OpenIddictTokenStore <, , , ,>).MakeGenericType(
                           /* TToken: */ key,
                           /* TApplication: */ root.GenericTypeArguments[1],
                           /* TAuthorization: */ root.GenericTypeArguments[2],
                           /* TContext: */ typeof(TContext),
                           /* TKey: */ root.GenericTypeArguments[0]));
            });

            return((IOpenIddictTokenStore <TToken>)_provider.GetRequiredService(type));
        }
        /// <summary>
        /// Returns a token store compatible with the specified token type or throws an
        /// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
        /// </summary>
        /// <typeparam name="TToken">The type of the Token entity.</typeparam>
        /// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
        public IOpenIddictTokenStore <TToken> Get <TToken>() where TToken : class
        {
            var store = _provider.GetService <IOpenIddictTokenStore <TToken> >();

            if (store != null)
            {
                return(store);
            }

            var type = _cache.GetOrAdd(typeof(TToken), key =>
            {
                var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken <, ,>));
                if (root == null)
                {
                    throw new InvalidOperationException(new StringBuilder()
                                                        .AppendLine("The specified token type is not compatible with the Entity Framework 6.x stores.")
                                                        .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in ")
                                                        .Append("'OpenIddictToken' entity (from the 'OpenIddict.EntityFramework.Models' package) ")
                                                        .Append("or a custom entity that inherits from the generic 'OpenIddictToken' entity.")
                                                        .ToString());
                }

                var context = _options.CurrentValue.ContextType;
                if (context == null)
                {
                    throw new InvalidOperationException(new StringBuilder()
                                                        .AppendLine("No Entity Framework 6.x context was specified in the OpenIddict options.")
                                                        .Append("To configure the OpenIddict Entity Framework 6.x stores to use a specific 'DbContext', ")
                                                        .Append("use 'options.AddEntityFrameworkStores().UseContext<TContext>()'.")
                                                        .ToString());
                }

                return(typeof(OpenIddictTokenStore <, , , ,>).MakeGenericType(
                           /* TToken: */ key,
                           /* TApplication: */ root.GenericTypeArguments[1],
                           /* TAuthorization: */ root.GenericTypeArguments[2],
                           /* TContext: */ context,
                           /* TKey: */ root.GenericTypeArguments[0]));
            });

            return((IOpenIddictTokenStore <TToken>)_provider.GetRequiredService(type));
        }
        /// <summary>
        /// Replace the default token manager by a custom manager
        /// derived from <see cref="OpenIddictTokenManager{TToken}"/>.
        /// Note: when using this overload, the token manager can be
        /// either a non-generic, a closed or an open generic service.
        /// </summary>
        /// <param name="type">The type of the custom manager.</param>
        /// <param name="lifetime">The lifetime of the registered service.</param>
        /// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
        public OpenIddictCoreBuilder ReplaceTokenManager(
            [NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager <>));

            if (root == null)
            {
                throw new ArgumentException("The specified type is invalid.", nameof(type));
            }

            // Note: managers can be either open generics (e.g OpenIddictTokenManager<>)
            // or closed generics (e.g OpenIddictTokenManager<OpenIddictToken>).
            if (type.IsGenericTypeDefinition)
            {
                if (type.GetGenericArguments().Length != 1)
                {
                    throw new ArgumentException("The specified type is invalid.", nameof(type));
                }

                Services.Replace(new ServiceDescriptor(type, type, lifetime));
                Services.Replace(new ServiceDescriptor(typeof(OpenIddictTokenManager <>), type, lifetime));
            }

            else
            {
                object ResolveManager(IServiceProvider provider)
                => provider.GetRequiredService(typeof(OpenIddictTokenManager <>)
                                               .MakeGenericType(root.GenericTypeArguments[0]));

                Services.Replace(new ServiceDescriptor(type, ResolveManager, lifetime));
                Services.Replace(new ServiceDescriptor(typeof(OpenIddictTokenManager <>)
                                                       .MakeGenericType(root.GenericTypeArguments[0]), type, lifetime));
            }

            return(this);
        }