概述
NopCommerce源码架构详解-自定义系统启动时执行任务Task相关源码分析。
内容
看过我之前写的AutoMapper在nop中的运用:NopCommerce源码架构详解-AutoMapper对象关联映射相关源码分析,对于Nop的任务Task并不陌生,因为之前就提到过IStartupTask接口和AutoMapperStartupTask。今天单独写篇文章来分析一下Nop中的自定义系统启动时执行任务Task相关源码。
相关功能主要用到的类或接口:
1、IStartupTask
2、AutoMapperStartupTask、EfStartUpTask
3、IEngine
4、NopEngine
5、EngineContext
6、Global.asax
1、IStartupTask
namespace Nop.Core.Infrastructure { /// <summary> /// Interface which should be implemented by tasks run on startup /// </summary> public interface IStartupTask { /// <summary> /// Execute task /// </summary> void Execute(); /// <summary> /// Order /// </summary> int Order { get; } } }
IStartupTask接口是所有的启动任务的抽象接口,所有的自定义启动任务都要实现这个接口。
2、AutoMapperStartupTask、EfStartUpTask
AutoMapperStartupTask和EfStartUpTask接口IStartupTask的真正实现,所有的逻辑都可以写在里面。如EfStartUpTask.cs:
using Nop.Core; using Nop.Core.Data; using Nop.Core.Infrastructure; namespace Nop.Data { public class EfStartUpTask : IStartupTask { public void Execute() { var settings = EngineContext.Current.Resolve<DataSettings>(); if (settings != null && settings.IsValid()) { var provider = EngineContext.Current.Resolve<IDataProvider>(); if (provider == null) throw new NopException("No IDataProvider found"); provider.SetDatabaseInitializer(); } } public int Order { //ensure that this task is run first get { return -1000; } } } }
EfStartUpTask是初始化EF相关的数据库的操作。
3、IEngine
using System; using Nop.Core.Configuration; using Nop.Core.Infrastructure.DependencyManagement; namespace Nop.Core.Infrastructure { /// <summary> /// Classes implementing this interface can serve as a portal for the /// various services composing the Nop engine. Edit functionality, modules /// and implementations access most Nop functionality through this /// interface. /// </summary> public interface IEngine { /// <summary> /// Container manager /// </summary> ContainerManager ContainerManager { get; } /// <summary> /// Initialize components and plugins in the nop environment. /// </summary> /// <param name="config">Config</param> void Initialize(NopConfig config); /// <summary> /// Resolve dependency /// </summary> /// <typeparam name="T">T</typeparam> /// <returns></returns> T Resolve<T>() where T : class; /// <summary> /// Resolve dependency /// </summary> /// <param name="type">Type</param> /// <returns></returns> object Resolve(Type type); /// <summary> /// Resolve dependencies /// </summary> /// <typeparam name="T">T</typeparam> /// <returns></returns> T[] ResolveAll<T>(); } }
IEngine是Nop的引擎的抽象接口,抽象提出了一些公共方法,如:设置IoC容器,从IoC容器中获取组件,以及初始化组件、插件到Nop环境。
4、NopEngine
NopEngine类实现了接口IEngine,真正的实现了接口的方法。我们可以在里面看到有两个相关于StartupTask的方法:
// <summary> /// Run startup tasks /// </summary> protected virtual void RunStartupTasks() { var typeFinder = _containerManager.Resolve<ITypeFinder>(); var startUpTaskTypes = typeFinder.FindClassesOfType<IStartupTask>(); var startUpTasks = new List<IStartupTask>(); foreach (var startUpTaskType in startUpTaskTypes) startUpTasks.Add((IStartupTask)Activator.CreateInstance(startUpTaskType)); //sort startUpTasks = startUpTasks.AsQueryable().OrderBy(st => st.Order).ToList(); foreach (var startUpTask in startUpTasks) startUpTask.Execute(); } /// <summary> /// Initialize components and plugins in the nop environment. /// </summary> /// <param name="config">Config</param> public void Initialize(NopConfig config) { //register dependencies RegisterDependencies(config); //startup tasks if (!config.IgnoreStartupTasks) { RunStartupTasks(); } }
可以看到上面通过typeFinder找到所有实现接口IStartupTask的组件,然后通过排序,最后依次调用其Execute()方法。
5、EngineContext
EngineContext提供了访问Nop engine的单例入口。
/// <summary> /// Creates a factory instance and adds a http application injecting facility. /// </summary> /// <param name="config">Config</param> /// <returns>New engine instance</returns> protected static IEngine CreateEngineInstance(NopConfig config) { if (config != null && !string.IsNullOrEmpty(config.EngineType)) { var engineType = Type.GetType(config.EngineType); if (engineType == null) throw new ConfigurationErrorsException("The type '" + config.EngineType + "' could not be found. Please check the configuration at /configuration/nop/engine[@engineType] or check for missing assemblies."); if (!typeof(IEngine).IsAssignableFrom(engineType)) throw new ConfigurationErrorsException("The type '" + engineType + "' doesn't implement 'Nop.Core.Infrastructure.IEngine' and cannot be configured in /configuration/nop/engine[@engineType] for that purpose."); return Activator.CreateInstance(engineType) as IEngine; } return new NopEngine(); } /// <summary> /// Initializes a static instance of the Nop factory. /// </summary> /// <param name="forceRecreate">Creates a new factory instance even though the factory has been previously initialized.</param> [MethodImpl(MethodImplOptions.Synchronized)] public static IEngine Initialize(bool forceRecreate) { if (Singleton<IEngine>.Instance == null || forceRecreate) { var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; Singleton<IEngine>.Instance = CreateEngineInstance(config); Singleton<IEngine>.Instance.Initialize(config); } return Singleton<IEngine>.Instance; }
将方法Initialize用 [MethodImpl(MethodImplOptions.Synchronized)] 标识,表示该方法一次只能由一个线程执行,避免多线程创建多个IEngine实例。创建IEngine实例时调用方法CreateEngineInstance,根据Web.config的配置创建相应的IEngine实例。如果在Web.config没有指定Engine的类型,就创建默认的Engine,即NopEngine。由于Nop默认在配置文件中是没有指定Engine的类型的。如下图:

从上图可以看到Engine结点属性Type的值为空,所以EngineContext的CreateEngineInstance会创建一个NopEngine的实例。当然你也可以自己定义一个类并实现接口IEngine,然后在Web.config中在Engine结点中为属性Type填上相应的值。
在创建了相应IEngine的实例之后,最后会调用其Initialize方法(Singleton.Instance.Initialize(config);),也就会执行到任务的startUpTask.Execute();
6、Global.asax
在程序启动时Global.asax的Application_Start方法会运行,我们可以看到在Application_Start中会有一行代码:
//initialize engine context EngineContext.Initialize(false);
这行代码是启动时执行任务Task的最开始的地方,会一层层地往下执行,直到执行到AutoMapperStartupTask和EfStartUpTask的Execute()方法。