/// <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)); }