/// <summary>
    /// This method asynchronously loads an instance of <see cref="AsyncResourceFactory{TResource}"/> using this <see cref="ResourceFactoryDynamicCreationConfiguration"/> along with required callbacks.
    /// </summary>
    /// <typeparam name="TResource">The type of resource that returned <see cref="AsyncResourceFactory{TResource}"/> provides.</typeparam>
    /// <param name="configuration">This <see cref="ResourceFactoryDynamicCreationConfiguration"/>.</param>
    /// <param name="assemblyLoader">The callback to asynchronously load assembly. The parameters are, in this order: package ID, package version, and path within the package.</param>
    /// <param name="creationParametersProvider">The callback to create creation parameters to bind the returned <see cref="AsyncResourceFactory{TResource}"/> to.</param>
    /// <param name="token">The <see cref="CancellationToken"/> for this asynchronous operation.</param>
    /// <returns>Asynchronously returns instance of <see cref="AsyncResourceFactory{TResource}"/>, or throws an exception.</returns>
    /// <exception cref="NullReferenceException">If this <see cref="ResourceFactoryDynamicCreationConfiguration"/> is <c>null.</c></exception>
    /// <exception cref="InvalidOperationException">If for some reason the <see cref="AsyncResourceFactory{TResource}"/> could not be loaded.</exception>
    /// <seealso cref="AcquireResourcePoolProvider"/>
    public static async Task <AsyncResourceFactory <TResource> > CreateAsyncResourceFactory <TResource>(
        this ResourceFactoryDynamicCreationConfiguration configuration,
        Func <String, String, String, CancellationToken, Task <Assembly> > assemblyLoader,
        Func <AsyncResourceFactoryProvider, Object> creationParametersProvider,
        CancellationToken token
        )
    {
        var factory = await ArgumentValidator.ValidateNotNullReference(configuration).AcquireResourcePoolProvider(assemblyLoader, token);

        var value = factory.GetFirstOrDefault();

        return((value ?? throw new InvalidOperationException(factory.GetSecondOrDefault() ?? "Unspecified error."))
               .BindCreationParameters <TResource>(creationParametersProvider(value)));
    }
    /// <summary>
    /// This method asynchornously loads an instance of <see cref="AsyncResourceFactoryProvider"/> using this <see cref="ResourceFactoryDynamicCreationConfiguration"/> along with assembly loader callback.
    /// </summary>
    /// <param name="configuration">This <see cref="ResourceFactoryDynamicCreationConfiguration"/>.</param>
    /// <param name="assemblyLoader">The callback to asynchronously load assembly. The parameters are, in this order: package ID, package version, and path within the package.</param>
    /// <param name="token">The <see cref="CancellationToken"/> for this asynchronous operation.</param>
    /// <returns>Asynchronously returns a value which either has <see cref="AsyncResourceFactoryProvider"/> or an error message.</returns>
    /// <exception cref="NullReferenceException">If this <see cref="ResourceFactoryDynamicCreationConfiguration"/> is <c>null</c>.</exception>
    public static async Task <EitherOr <AsyncResourceFactoryProvider, String> > AcquireResourcePoolProvider(
        this ResourceFactoryDynamicCreationConfiguration configuration,
        Func <String, String, String, CancellationToken, Task <Assembly> > assemblyLoader,
        CancellationToken token
        )
    {
        AsyncResourceFactoryProvider retVal = null;
        String errorMessage = null;

        if (configuration != null)
        {
            if (assemblyLoader != null)
            {
                var packageID = configuration.PoolProviderPackageID;
                if (!String.IsNullOrEmpty(packageID))
                {
                    try
                    {
                        var assembly = await assemblyLoader(
                            packageID,                              // package ID
                            configuration.PoolProviderVersion,      // optional package version
                            configuration.PoolProviderAssemblyPath, // optional assembly path within package
                            token
                            );

                        if (assembly != null)
                        {
                            // Now search for the type
                            var  typeName        = configuration.PoolProviderTypeName;
                            var  parentType      = typeof(AsyncResourceFactoryProvider).GetTypeInfo();
                            var  checkParentType = !String.IsNullOrEmpty(typeName);
                            Type providerType;
                            if (checkParentType)
                            {
                                // Instantiate directly
                                providerType = assembly.GetType(typeName); //, false, false );
                            }
                            else
                            {
                                // Search for first available
                                providerType = assembly.
#if NET40
                                               GetTypes()
#else
                                               DefinedTypes
#endif
                                               .FirstOrDefault(t => !t.IsInterface && !t.IsAbstract && t.IsPublic && parentType.IsAssignableFromIgnoreAssemblyVersion(t))
#if !NET40
                                               ?.AsType()
#endif
                                ;
                            }

                            if (providerType != null)
                            {
                                if (!checkParentType || parentType.IsAssignableFromIgnoreAssemblyVersion(providerType.GetTypeInfo()))
                                {
                                    // All checks passed, instantiate the pool provider
                                    retVal = (AsyncResourceFactoryProvider)Activator.CreateInstance(providerType);
                                }
                                else
                                {
                                    errorMessage = $"The type \"{providerType.FullName}\" in \"{assembly}\" does not have required parent type \"{parentType.FullName}\".";
                                }
                            }
                            else
                            {
                                errorMessage = $"Failed to find type within assembly in \"{assembly}\", try specify \"{nameof( configuration.PoolProviderTypeName )}\" configuration parameter.";
                            }
                        }
                        else
                        {
                            errorMessage = $"Failed to load resource pool provider package \"{packageID}\".";
                        }
                    }
                    catch (Exception exc)
                    {
                        errorMessage = $"An exception occurred when acquiring resource pool provider: {exc.Message}";
                    }
                }
                else
                {
                    errorMessage = $"No NuGet package ID were provided as \"{nameof( configuration.PoolProviderPackageID )}\" configuration parameter. The package ID should be of the package holding implementation for \"{nameof( AsyncResourceFactoryProvider )}\" type.";
                }
            }
            else
            {
                errorMessage = "Task must be provided callback to load NuGet packages (just make constructor taking it as argument and use UtilPack.NuGet.MSBuild task factory).";
            }
        }
        else
        {
            errorMessage = "Configuration was not provided.";
        }

        return(retVal == null ?
               new EitherOr <AsyncResourceFactoryProvider, String>(errorMessage) :
               new EitherOr <AsyncResourceFactoryProvider, String>(retVal));
    }