using System.Reflection; using AutoMapper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Nop.Core.Configuration; using Nop.Core.Infrastructure.Mapper; namespace Nop.Core.Infrastructure; /// /// Represents Nop engine /// public partial class NopEngine : IEngine { #region Utilities /// /// Get IServiceProvider /// /// IServiceProvider protected virtual IServiceProvider GetServiceProvider(IServiceScope scope = null) { if (scope == null) { var accessor = ServiceProvider?.GetService(); var context = accessor?.HttpContext; return context?.RequestServices ?? ServiceProvider; } return scope.ServiceProvider; } /// /// Run startup tasks /// protected virtual void RunStartupTasks() { //find startup tasks provided by other assemblies var typeFinder = Singleton.Instance; var startupTasks = typeFinder.FindClassesOfType(); //create and sort instances of startup tasks //we startup this interface even for not installed plugins. //otherwise, DbContext initializers won't run and a plugin installation won't work var instances = startupTasks .Select(startupTask => (IStartupTask)Activator.CreateInstance(startupTask)) .Where(startupTask => startupTask != null) .OrderBy(startupTask => startupTask.Order); //execute tasks foreach (var task in instances) task.Execute(); } /// /// Register and configure AutoMapper /// protected virtual void AddAutoMapper() { //find mapper configurations provided by other assemblies var typeFinder = Singleton.Instance; var mapperConfigurations = typeFinder.FindClassesOfType(); //create and sort instances of mapper configurations var instances = mapperConfigurations .Select(mapperConfiguration => (IOrderedMapperProfile)Activator.CreateInstance(mapperConfiguration)) .Where(mapperConfiguration => mapperConfiguration != null) .OrderBy(mapperConfiguration => mapperConfiguration.Order); //create AutoMapper configuration var config = new MapperConfiguration(cfg => { foreach (var instance in instances) cfg.AddProfile(instance.GetType()); }); //register AutoMapperConfiguration.Init(config); } protected virtual Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { var assemblyFullName = args.Name; //check for assembly already loaded var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == assemblyFullName); if (assembly != null) return assembly; //get assembly from TypeFinder var typeFinder = Singleton.Instance; return typeFinder?.GetAssemblyByName(assemblyFullName); } #endregion #region Methods /// /// Add and configure services /// /// Collection of service descriptors /// Configuration of the application public virtual void ConfigureServices(IServiceCollection services, IConfiguration configuration) { //register engine services.AddSingleton(this); //find startup configurations provided by other assemblies var typeFinder = Singleton.Instance; var startupConfigurations = typeFinder.FindClassesOfType(); //create and sort instances of startup configurations var instances = startupConfigurations .Select(startup => (INopStartup)Activator.CreateInstance(startup)) .Where(startup => startup != null) .OrderBy(startup => startup.Order); //configure services foreach (var instance in instances) instance.ConfigureServices(services, configuration); services.AddSingleton(services); //register mapper configurations AddAutoMapper(); //run startup tasks RunStartupTasks(); //resolve assemblies here. otherwise, plugins can throw an exception when rendering views AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } /// /// Configure HTTP request pipeline /// /// Builder for configuring an application's request pipeline public virtual void ConfigureRequestPipeline(IApplicationBuilder application) { ServiceProvider = application.ApplicationServices; //find startup configurations provided by other assemblies var typeFinder = Singleton.Instance; var startupConfigurations = typeFinder.FindClassesOfType(); //create and sort instances of startup configurations var instances = startupConfigurations .Select(startup => (INopStartup)Activator.CreateInstance(startup)) .Where(startup => startup != null) .OrderBy(startup => startup.Order); //configure request pipeline foreach (var instance in instances) instance.Configure(application); } /// /// Resolve dependency /// /// Scope /// Type of resolved service /// Resolved service public virtual T Resolve(IServiceScope scope = null) where T : class { return (T)Resolve(typeof(T), scope); } /// /// Resolve dependency /// /// Type of resolved service /// Scope /// Resolved service public virtual object Resolve(Type type, IServiceScope scope = null) { return GetServiceProvider(scope)?.GetService(type); } /// /// Resolve dependencies /// /// Type of resolved services /// Collection of resolved services public virtual IEnumerable ResolveAll() { return (IEnumerable)GetServiceProvider().GetServices(typeof(T)); } /// /// Resolve unregistered service /// /// Type of service /// Resolved service public virtual object ResolveUnregistered(Type type) { Exception innerException = null; foreach (var constructor in type.GetConstructors()) try { //try to resolve constructor parameters var parameters = constructor.GetParameters().Select(parameter => { var service = Resolve(parameter.ParameterType) ?? throw new NopException("Unknown dependency"); return service; }); //all is ok, so create instance return Activator.CreateInstance(type, parameters.ToArray()); } catch (Exception ex) { innerException = ex; } throw new NopException("No constructor was found that had all the dependencies satisfied.", innerException); } #endregion #region Properties /// /// Service provider /// public virtual IServiceProvider ServiceProvider { get; protected set; } #endregion }