文章目录
概述
NopCommerce源码架构详解-Themes网站主题实现源码分析2。
内容
我在前面一篇文章我们对讲了nop的Themes网站主题有了一个大概的概念。下面我们开始来看看到底里面的核心原理,分别针对里面重要的类进行一些关键点介绍。下面是主要的类的类图和方法。

可以从上图中看到这些类的关系。入口是ThemeContext类,它是针对接口IThemeContext的真正实现,接口很简单,它只有一个方法。
namespace Nop.Web.Framework.Themes { /// <summary> /// Work context /// </summary> public interface IThemeContext { /// <summary> /// 获取系统当前主题的名称 /// </summary> string WorkingThemeName { get; set; } } }
Nop.Web.Framework.Themes.ThemeContext.cs:
using System; using System.linq; using Nop.Core; using Nop.Core.Domain; using Nop.Core.Domain.Customers; using Nop.Services.Common; namespace Nop.Web.Framework.Themes { /// <summary> /// Theme context /// </summary> public partial class ThemeContext : IThemeContext { private readonly IWorkContext _workContext; private readonly IStoreContext _storeContext; private readonly IGenericAttributeService _genericAttributeService; private readonly StoreInformationSettings _storeInformationSettings; private readonly IThemeProvider _themeProvider; private bool _themeIsCached; private string _cachedThemeName; public ThemeContext(IWorkContext workContext, IStoreContext storeContext, IGenericAttributeService genericAttributeService, StoreInformationSettings storeInformationSettings, IThemeProvider themeProvider) { this._workContext = workContext; this._storeContext = storeContext; this._genericAttributeService = genericAttributeService; this._storeInformationSettings = storeInformationSettings; this._themeProvider = themeProvider; } /// <summary> /// 属性:读取或设置系统主题名称 /// </summary> public string WorkingThemeName { get { if (_themeIsCached) return _cachedThemeName; string theme = ""; if (_storeInformationSettings.AllowCustomerToSelectTheme) { if (_workContext.CurrentCustomer != null) theme = _workContext.CurrentCustomer.GetAttribute<string>(SystemCustomerAttributeNames.WorkingThemeName, _genericAttributeService, _storeContext.CurrentStore.Id); } //默认商店主题 if (string.IsNullOrEmpty(theme)) theme = _storeInformationSettings.DefaultStoreTheme; //ensure that theme exists if (!_themeProvider.ThemeConfigurationExists(theme)) { //如果不存在默认主题就加载主题集合中的第一个主题 var themeInstance = _themeProvider.GetThemeConfigurations() .FirstOrDefault(); if (themeInstance == null) throw new Exception("No theme could be loaded"); theme = themeInstance.ThemeName; } //cache theme this._cachedThemeName = theme; this._themeIsCached = true; return theme; } set { if (!_storeInformationSettings.AllowCustomerToSelectTheme) return; if (_workContext.CurrentCustomer == null) return; _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.WorkingThemeName, value, _storeContext.CurrentStore.Id); //clear cache this._themeIsCached = false; } } } }
注意:类ThemeContext构造函数的参数都是接口类型,这样可以通过Autofac进行依赖注入,从而降低了对具体实现的耦合。最后一个参数为IThemeProvider类型。
Nop.Web.Framework.Themes.IThemeProvider:
System.Collections.Generic; namespace Nop.Web.Framework.Themes { public partial interface IThemeProvider { ThemeConfiguration GetThemeConfiguration(string themeName);//获取指定主题配置信息 IList<ThemeConfiguration> GetThemeConfigurations();//获取全部主题的配置信息集合 bool ThemeConfigurationExists(string themeName);//判断主题是否存在 } }
Nop.Web.Framework.Themes.ThemeProvider::
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using Nop.Core; namespace Nop.Web.Framework.Themes { /// <summary> /// 主题全部集合:项目的路径~/Themes/,一个主题一个目录,遍历目录并加载每天个主题目录下面的配置信息theme.config /// </summary> public partial class ThemeProvider : IThemeProvider { #region Fields private readonly IList<ThemeConfiguration> _themeConfigurations = new List<ThemeConfiguration>(); private readonly string _basePath = string.Empty; #endregion #region Constructors public ThemeProvider() { _basePath = CommonHelper.MapPath("~/Themes/"); LoadConfigurations(); } #endregion #region IThemeProvider public ThemeConfiguration GetThemeConfiguration(string themeName) { return _themeConfigurations .SingleOrDefault(x => x.ThemeName.Equals(themeName, StringComparison.InvariantCultureIgnoreCase)); } public IList<ThemeConfiguration> GetThemeConfigurations() { return _themeConfigurations; } public bool ThemeConfigurationExists(string themeName) { return GetThemeConfigurations().Any(configuration => configuration.ThemeName.Equals(themeName, StringComparison.InvariantCultureIgnoreCase)); } #endregion #region Utility /// <summary> /// 从主题所在根目录开始遍历,并加载这些主题 /// </summary> private void LoadConfigurations() { //TODO:Use IFileStorage? foreach (string themeName in Directory.GetDirectories(_basePath)) { var configuration = CreateThemeConfiguration(themeName); if (configuration != null) { _themeConfigurations.Add(configuration); } } } /// <summary> /// 读取主题配置文件里面配置信息并封装成一个对象ThemeConfiguration /// </summary> /// <param name="themePath"></param> /// <returns></returns> private ThemeConfiguration CreateThemeConfiguration(string themePath) { var themeDirectory = new DirectoryInfo(themePath); var themeConfigFile = new FileInfo(Path.Combine(themeDirectory.FullName, "theme.config")); if (themeConfigFile.Exists) { var doc = new XmlDocument(); doc.Load(themeConfigFile.FullName); return new ThemeConfiguration(themeDirectory.Name, themeDirectory.FullName, doc); } return null; } #endregion } }
可以看到的有插件的根目录为~/Themes/,并在构造函数中调用LoadConfigurations方法来加载检测到的插件的的有配置信息。