NopCommerce源码架构详解-自定义系统启动时执行任务Task相关源码分析

NopCommerce源码架构详解-自定义系统启动时执行任务Task相关源码分析

6066发表于2015-10-20

看过我之前写的AutoMappernop中的运用:NopCommerce源码架构详解-AutoMapper对象关联映射相关源码分析,对于Nop的任务Task并不陌生,因为之前就提到过IStartupTask接口和AutoMapperStartupTask。今天单独写篇文章来分析一下Nop中的自定义系统启动时执行任务Task相关源码。

相关功能主要用到的类或接口:

1、IStartupTask

2、AutoMapperStartupTask、EfStartUpTask

3、IEngine

4、NopEngine

5、EngineContext

6、Global.asax


一、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接口是所有的启动任务的抽象接口,所有的自定义启动任务都要实现这个接口。

二、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相关的数据库的操作。

三、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环境。

四、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()方法。

五、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<IEngine>.Instance.Initialize(config);),也就会执行到任务的startUpTask.Execute();

六、Global.asax

在程序启动时Global.asax的Application_Start方法会运行,我们可以看到在Application_Start中会有一行代码:

 //initialize engine context
 EngineContext.Initialize(false);

这行代码是启动时执行任务Task的最开始的地方,会一层层地往下执行,直到执行到AutoMapperStartupTask和EfStartUpTask的Execute()方法。


小编蓝狐