首页 nopcommerce 系统开发文档

nopcommerce 系统开发文档

举报
开通vip

nopcommerce 系统开发文档nopcommerce 系统开发文档 nopcommerce中的主题模块 利用nopcommerce中的主题模块(或者叫“皮肤”,“模板”),我们可以很方便地从管理界面选择当前主题。 今天把主题这块的知识整理一下,同时也起个抛砖引玉的作用。 1.如何使用。 主题的使用很简单,只有3步: a 上传你的新主题到themes文件夹下 b把head.cshtml中的CSS路径换成你自己的,这个在开发主题的时候应该就搞定了 c 在后台管理面板中选择你新的主题,保存,然后你就能在前台看到了。 2.它是如何加载的,...

nopcommerce 系统开发文档
nopcommerce 系统开发文档 nopcommerce中的主题模块 利用nopcommerce中的主题模块(或者叫“皮肤”,“ 模板 个人简介word模板免费下载关于员工迟到处罚通告模板康奈尔office模板下载康奈尔 笔记本 模板 下载软件方案模板免费下载 ”),我们可以很方便地从管理界面选择当前主题。 今天把主题这块的知识整理一下,同时也起个抛砖引玉的作用。 1.如何使用。 主题的使用很简单,只有3步: a 上传你的新主题到themes文件夹下 b把head.cshtml中的CSS路径换成你自己的,这个在开发主题的时候应该就搞定了 c 在后台管理面板中选择你新的主题,保存,然后你就能在前台看到了。 2.它是如何加载的, 这是本文重点。 说到加载机制就要说到主题模块用到的三个类,它们都位于Nop.Web.Framework.Themes下: 继承自下边一个类 ThemeableRazorViewEngine - ThemeableBuildManagerViewEngine - 继承自下边一个类 ThemeableVirtualPathProviderViewEngine 然后是起到触发作用的Global.asax.cs 那么nopcommerce是一个MVC项目,大家都清楚视图文件都是放在view文件夹下的,为嘛nopcommerce 能直接读取theme里的视图哪, OK先来说这三个和主题相关的类是怎么回事。 首先看ThemeableVirtualPathProviderViewEngine,它是从VirtualPathProviderViewEngine类继承的,使用此类可以改变所谓的视图的对应文件夹的实际位置,比如: public ThemeableRazorViewEngine() { AreaViewLocationFormats = new[] { //themes "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.vbhtml", //default "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaMasterLocationFormats = new[] { //themes "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.vbhtml", //default "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaPartialViewLocationFormats = new[] { //themes "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Themes/{3}/Views/Shared/{0}.vbhtml", //default "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; ViewLocationFormats = new[] { //themes "~/Themes/{2}/Views/{1}/{0}.cshtml", "~/Themes/{2}/Views/{1}/{0}.vbhtml", "~/Themes/{2}/Views/Shared/{0}.cshtml", "~/Themes/{2}/Views/Shared/{0}.vbhtml", //default "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml", //Admin "~/Administration/Views/{1}/{0}.cshtml", "~/Administration/Views/{1}/{0}.vbhtml", "~/Administration/Views/Shared/{0}.cshtml", "~/Administration/Views/Shared/{0}.vbhtml", }; MasterLocationFormats = new[] { //themes "~/Themes/{2}/Views/{1}/{0}.cshtml", "~/Themes/{2}/Views/{1}/{0}.vbhtml", "~/Themes/{2}/Views/Shared/{0}.cshtml", "~/Themes/{2}/Views/Shared/{0}.vbhtml", //default "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; PartialViewLocationFormats = new[] { //themes "~/Themes/{2}/Views/{1}/{0}.cshtml", "~/Themes/{2}/Views/{1}/{0}.vbhtml", "~/Themes/{2}/Views/Shared/{0}.cshtml", "~/Themes/{2}/Views/Shared/{0}.vbhtml", //default "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml", //Admin "~/Administration/Views/{1}/{0}.cshtml", "~/Administration/Views/{1}/{0}.vbhtml", "~/Administration/Views/Shared/{0}.cshtml", "~/Administration/Views/Shared/{0}.vbhtml", }; FileExtensions = new[] { "cshtml", "vbhtml" }; } 以上代码不难看出,除了nopcommerce的主题,还有管理界面啊之类的都定好了View是在哪个物理路 径上。 很自然地,我们在global文件里,应用启动的时候调用这么一段话就可以让nopcommerce把主题定向 theme文件夹: if (databaseInstalled) { //remove all view engines ViewEngines.Engines.Clear(); //except the themeable razor view engine we use ViewEngines.Engines.Add(new ThemeableRazorViewEngine()); } VirtualPathProviderViewEngine其实不是一个新东西,很早就有了,而且不但是MVC,就连webform也可以用它。更多信息,*丝们可以猛击此处 3.扩展主题机制。 既然知道原理,我们就可以着手扩展,这个就是个大题目了,如何扩展在具体个人,我们来举2个例子: 1默认管理路径为administration,我们可以改成自己想要的路径 2主题不定要放到Themes里,我们修改代码可以让它从其它地方读取视图文件。进一步来讲还可以把视图文件存放到数据库中。 NopCommerce里的 计划 项目进度计划表范例计划下载计划下载计划下载课程教学计划下载 任务机制 Nop.Web.Global.asax.cs //start scheduled tasks if (databaseInstalled) { TaskManager.Instance.Initialize(); TaskManager.Instance.Start(); } 做一个你自己的计划任务 1、依照下述(三、)中所列,为YourNewTask类做好路径,它应该有ITask接口; 2、YourNewTask只有一个无参数的方法:Execute。在任务要执行的时候,这个方法会被调用。 3、数据表ScheduleTask中添加一条记录。你可以手动添加,或使用IScheduleTaskService来添加记录。 现有的任务:(nop 2.50) 1 Send emails 60 Nop.Services.Messages.QueuedMessagesSendTask, Nop.Services 2 Keep alive 300 Nop.Services.Common.KeepAliveTask, Nop.Services 3 Delete guests 600 Nop.Services.Customers.DeleteGuestsTask, Nop.Services 4 Clear cache 600 Nop.Services.Caching.ClearCacheTask, Nop.Services 5 Update currency exchange rates 900 Nop.Services.Directory.UpdateExchangeRateTask, Nop.Services 6 MailChimp sync 3600 Nop.Plugin.Misc.MailChimp.MailChimpSynchronizationTask, Nop.Plugin.Misc.MailChimp 7 Froogle static file generation 3600 Nop.Plugin.Feed.Froogle.StaticFileGenerationTask, Nop.Plugin.Feed.Froogle nopcommerce计划任务分析 对比了一下nopcommerce和orchard的计划任务,orchard的复杂的不是一点点,如果想拆下来自己用难度很大,搜索拆了 orchard的lucene处理模块,邮件队列拆的discuznt和nopcommerce的结合,计划任务就拆nopcommerce的 了,discuznt计划任务设计的没nopcommerce的好。 1.nopcommerce的tasks结构如下: IScheduleTaskService.cs 接口,这个主要是获取数据库里的任务信息,ScheduleTaskService.cs去实现它就可以了,当然需要在容器里注入一下。 ITask 这个接口比较特别但是很重要,所有的任务处理类都要实现里面唯一的Execute方法。执行计划任务时就需要通过反射来执行这个实现。 namespace Nop.Services.Tasks { /// /// Interface that should be implemented by each task /// public partial interface ITask { /// /// Execute task /// void Execute(); } } 核心类之一:Task.cs,这个主要是处理任务的执行过程及执行过程类的结果处理。 private ITask CreateTask() { ITask task = null; if (this.Enabled) { var type2 = System.Type.GetType(this.Type); if (type2 != null) { object instance; if (!EngineContext.Current.ContainerManager.TryResolve(type2, out instance)) { //not resolved instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2); } task = instance as ITask; } } return task; } 通过反射来找到编写的计划任务类。例如下面的发送邮件的任务。 using System; using Nop.Services.Logging; using Nop.Services.Tasks; namespace Nop.Services.Messages { /// /// Represents a task for sending queued message /// public partial class QueuedMessagesSendTask : ITask { private readonly IQueuedEmailService _queuedEmailService; private readonly IEmailSender _emailSender; private readonly ILogger _logger; public QueuedMessagesSendTask(IQueuedEmailService queuedEmailService, IEmailSender emailSender, ILogger logger) { this._queuedEmailService = queuedEmailService; this._emailSender = emailSender; this._logger = logger; } /// /// Executes a task /// public void Execute() { var maxTries = 3; var queuedEmails = _queuedEmailService.SearchEmails(null, null, null, null, true, maxTries, false, 0, 500); foreach (var queuedEmail in queuedEmails) { var bcc = String.IsNullOrWhiteSpace(queuedEmail.Bcc) ? null : queuedEmail.Bcc.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); var cc = String.IsNullOrWhiteSpace(queuedEmail.CC) ? null : queuedEmail.CC.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); try { _emailSender.SendEmail(queuedEmail.EmailAccount, queuedEmail.Subject, queuedEmail.Body, queuedEmail.From, queuedEmail.FromName, queuedEmail.To, queuedEmail.ToName, bcc, cc); queuedEmail.SentOnUtc = DateTime.UtcNow; } catch (Exception exc) { _logger.Error(string.Format("Error sending e-mail. {0}", exc.Message), exc); } finally { queuedEmail.SentTries = queuedEmail.SentTries + 1; _queuedEmailService.UpdateQueuedEmail(queuedEmail); } } } } } 执行完任务后需要将数据库里的任务记录状态更改,主要是时间状态变更。 核心执行方法: /// /// Executes the task /// public void Execute() { this.IsRunning = true; var scheduleTaskService = EngineContext.Current.Resolve(); var scheduleTask = scheduleTaskService.GetTaskByType(this.Type); try { var task = this.CreateTask(); if (task != null) { this.LastStartUtc = DateTime.UtcNow; if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastStartUtc = this.LastStartUtc; scheduleTaskService.UpdateTask(scheduleTask); } //execute task task.Execute(); this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow; } } catch (Exception exc) { this.Enabled = !this.StopOnError; this.LastEndUtc = DateTime.UtcNow; //log error var logger = EngineContext.Current.Resolve(); logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc); } if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastEndUtc = this.LastEndUtc; scheduleTask.LastSuccessUtc = this.LastSuccessUtc; scheduleTaskService.UpdateTask(scheduleTask); } this.IsRunning = false; } 任务管理类:TaskManager.cs,主要负责任务的初始化,添加到线程列表,任务的开始和停止。需要 在Global里初始化和开始任务,它会根据线程里的定时器自动读取任务列表执行任务。 //start scheduled tasks if (databaseInstalled) { TaskManager.Instance.Initialize(); TaskManager.Instance.Start(); } 任务线程管理类:TaskThread.cs,任务线程类,TaskManager将任务都添加到此线程管理类里,此线程 管理主要负责判断任务的执行状态,线程执行间隔时间及调用任务执行的主方法Execute,通过Timer 定时器实现定时自动运行。 主方法为: private void Run() { if (Seconds <= 0) return; this.StartedUtc = DateTime.UtcNow; this.IsRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this.IsRunning = false; } 从任务列表中读取任务并执行。 在NopCommerce中新增一个Domain Model的步骤 1. 在NopCommerce中新增一个Domain Model,需要以下几个步骤: 2. 新建一个Entity Class (Nop/Core/Domain/Entity.cs) 3. 新建一个Mapping Class (Nop/Data/Mapping/EntityMap.cs) 4. 新建一个View Model (Nop/Admin/Models/EntityModel.cs 或 Nop/Web/Models/EntityModel.cs) 5. 新建Model Validator (Nop/Admin/Validators/EntityValidator.cs 或 Nop/Web/Validators/EntityValidator.cs) 6. 为AutoMapper新建映射配置,用来完成Model和Entity之间的转换 (Nop/Admin/Infrastructure/AutoMapperStartupTask.cs 或 Nop/Web/Infrastructure/AutoMapperStartupTask.cs) 7. 编写ToModel和ToEntity (Nop/Admin/MappingExtensions.cs 或 Nop/Web/MappingExtensions.cs) 8. 创建Service和Service Interface (Nop/Services/EntityService.cs 和 Nop/Services/IEntityService.cs) 9. 最后新建的Model创建Controller和View PS: NopCommerce不支持database migration,需要手动更新数据库。 参考资料: NopCommerce MVC 插件机制分析 基本原理 插件化的应用程序一般都是先定义插件接口,然后把插件编译的dll放到固定的目录中,应用程序主程序通过加载那些实现了插件接口的dll来实现插件的使用。NopCommerce也是这样,但作为MVC Web应用程序会有一些不一样,首先是不同信任级别(Full Trust,Medium Trust)的时候加载dll的策略会有不一样,另外就是怎样显示插件中的View的问题。 放插件的文件夹 NopCommerce的插件放在网站主目录的Plugins目录下,Plugins下面有很多文件夹,一个插件类库就是一个文件夹。在插件类库中修改编译输出的地址为网站主目录的Plugins文件夹,这样插件生成的dll就能自动在目标文件夹下面。如下图: 另一个文件夹是ShadowCopy文件夹,就在Plugins/bin文件夹下。关于为什么要用ShadowCopy,住这篇文章中有所叙述,NopCommerce就是参考它的实现。里面也详细叙述了信任级别的问题。 网站启动 我们先来看PluginManager.Initialize方法。通过在PluginManager类上定义如下属性保证PluginManager的Initialize方法在网站开始的时候运行,早于Application_Start运行。 [assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")] 在Application_Start之前运行初始化代码主要是为了让网站应用程序可以引用到加载的dll。 核心类 IPluginFinder.cs接口:获取插件的信息接口,在ioc里的Nop.Web.Framework.DependencyRegistrar注册此接口。系统启动的时候会加载到内存里。 IPlugin.cs:插件的操作接口,主要有设置插件的属性信息,安装插件接口,卸载插件接口。 BasePlugins.cs 实现IPlugin.cs的方法。 PluginDescriptor.cs 插件的实体类,包含了插件的版本、描述,类型,文件名称,作者,等等一系列状态。 PluginFileParser.cs 包含对插件的实体操作方法,主要是写入插件的描述信息。 PluginFinder.cs 加载所有的插件,并获取它们的信息. PluginManager.cs 插件管理的主类,看里面的注释,它的插件机制应该是参考的Umbraco这个cms的。 加载插件 首先加载插件的描述。每一个插件都必须定义一个插件描述文件,用文本文件Description.txt来定义,名字也是约定的,不能是其他名字。 在插件的类库中添加Description.txt,对插件进行描述。Description中的文本字段的格式是固定的,PluginManager会 把Description.txt文件转化成PluginDescriptor类,然后存储在内存中。Description中的 DisplayOrder字段表示了这个插件的顺序,以便在界面上显示。获取所有的插件描述文件后,它就会去InstalledPlugins.txt里面看,在 InstalledPlugins.txt里面有的就是已经安装的查件,没有的话就是没有安装的。 PluginDescriptor.Installed属性描述了这个信息。 把需要加载的dll复制到Plugins/bin文件夹下,当然都是要继承自IPlugIn接口的,通过Assembly.Load在家这个 dll,再用BuildManager.AddReferencedAssembly把这个dll加载到网站这个应用程序中。要注意的是 BuildManager.AddReferencedAssembly必须早网站程序的Application_PreStartInit过程中加入, 也就是在Application_Start前。这是要引用的插件都被放在了Plugins/bin下,并被CLR引用了。被引用的插件的 Assembly引用将被保存在PluginManager的静态列表中ReferencedPlugins。PluginManager的 Initialize方法到此结束。 在界面上显示插件 举例来说,我们在定义插件的时候会定义一种类型的插件,比如送货方式。那么我们在定义插件的时候会继承2个接口,一个是IPlugin接口,一个是 IShippingMethod接口。在需要显示送货方式插件的时候通过PluginFinder.GetPlugins ().ToList() 方法去获取。PluginFinder会去上一步的PluginManager.ReferencedPlugins列表里面去寻找。返回的是 IShippingMethod的实例。NopCommerce有个txt文件:InstalledPlugs.txt。只有在这个里面的插件最终会加载 到界面上去。可以通过NopCommerce的查件管理页面把最终需要作用于网站的插件加入到这个文件中。 配置插件 NopCommerce的admin网站可以对插件进行配置。如下图:可以配置Display Order 和 IsActive等。主要的逻辑是更新该插件的Description.txt文件和内存中的IPlugin.Descriptor里面的属性。 定义插件中的Controller,Action和View 稍微复杂的插件基本都包含自己要处理的界面和逻辑。所以在插件的类库中可以定义插件的界面View和相关Controller和Action。在建 立Controller和View的时候,不一定要按照 规范 编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载 的Controllers文件夹和Views文件夹来定义,可以定义自己的风格。 在Action中返回View的时候,要输入View的名称,这个名称要包含名称空间,例如: return View("Nop.Plugin.DiscountRules.HasAllProducts.Views.DiscountRulesHasAllProducts.Configure", model); 因为,在编译过后的插件dll中,作为嵌入资源的View会被编译成名叫Nop.Plugin.Payments.CashOnDelivery.Views.PaymentCashOnDelivery.Configure.cshtml的资源文件。 NopCommerce通过一些插件的Configure界面,把一些插件的配置信息保存到数据库中。然后在前台页面显示的时候再从数据库获取。 读取嵌入的资源View 插件作为一个类库被加载到应用程序域中。而在定义插件的View的时候,需要把cshtml文件的属性修改成Embedded Resource。它是作为嵌入式资源放到AppDomain中去的。我们可以通过VirtualPathProvider,使 Web 应用程序可以从虚拟文件系统中检索资源,您可以在这篇文 章中找到相关知识,NopCommerce的实现和这篇文章是一样的。在NopCommerce中的Nop.Web.Framework类库中有个 EmbeddedViews文件夹,里面包含了如果处理嵌入的View的一些类。最后需要在Global.asax进行注册。NopCommerce的代 码如下: //register virtual path provider for embedded views var embeddedViewResolver = EngineContext.Current.Resolve(); var embeddedProvider = new EmbeddedViewVirtualPathProvider(embeddedViewResolver.GetEmbeddedViews()); HostingEnvironment.RegisterVirtualPathProvider(embeddedProvider); 编写NopCommerce插件 可以参考官方文档。其中有一条建议非常好,就是Copy原来的插件,在上面修改。 NopCommerce是如何使用Autofac实现依赖注入的 IOC和DI IOC中文名被称作控制反转(Inversion of Control),DI被称为依赖注入(Dependency Injection),可参考Martin Fowler的这篇文章来了解这两个概念:IoC容器和DependencyInjection模式。使用控制反转模式开发项目流程是先建立接口,然后再实现类,或许有人不习惯这样的开发方法,但在规模较大的软件架构中,这种方法却可以有效的降低类之间的互相依赖的情况,不但能增加架构的弹性,也能有效的降低软件的复杂度。 如果不考虑控制反转的情况,采用直接创建类,并直接在应用层调用该类,如此一来,应用层的对象就会与BLL(业务逻辑层)对象高度依赖,这样的依赖 会导致这两个类无法拆开,从而增加了这个类的维护难度,同时导致了单元测试难以进行。为了解决耦合度问题,从而引入了控制反转的概念。 Autofac介绍 Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET、Unity、Castle等,它更显得轻量级,同时保证了高性能。它具有以下优点: , 和C#语言联系紧密,可以使用C#语言的很多特性,譬如Lambda表达式等; , 较低的学习曲线,只需了解IoC和DI的概念以及在何时需要使用它们即可; , XML配置支持; , 自动装配; , 与ASP.NET MVC3集成;(Orchard也是使用Autofac实现IOC的) 在MVC3项目中使用Autofac 在MVC3工程中使用Autofac的最好也是最简单的方法是使用NuGet来安装Autofac.Mvc3,安装完成以后,在Global.asax的Application_Start方法中添加如下代码: protected void Application_Start() { var builder = new ContainerBuilder(); builder.RegisterControllers(typeof(MvcApplication).Assembly); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); // Other MVC setup... 这样就开启了Controller的依赖注入功能。其中的DependencyResolver是一个全局静态类,MVC3提供了对依赖注入的支 持,SetResolver函数用于设置使用哪个Resolver(解析器)来进行依赖注入,这里使用的是Autofac的依赖注入解析器。如果要使用自 己的解析器,必须在这里使用SetResolver函数设置。 1. 注册Controller 可以使用下面的方法对特定的Controller进行注册: var builder = new ContainerBuilder(); builder.RegisterType().InstancePerRequest(); 同时可以使用Autofac提供的RegisterControllers扩展方法来对程序集中所有的Controller一次性的完成注册: var builder = new ContainerBuilder(); builder.RegisterControllers(typeof(MvcApplication).Assembly); 2. 注册Model Binder 与控制器的注册类似,模型绑定也可以再Global.asax.cs中注册。您可以通过如下操作完成整个程序集的注册: var builder = newContainerBuilder(); builder.RegisterModelBinders(Assembly.GetExecutingAssembly()); builder.RegisterModelBinderProvider(); 您也必须记住使用RegisterModelBinderProvider扩展方法来注册RegisterModelBinderProvider。这个方法用是Autofac对IModelBinderProvider接口的实现。 因为RegisterModelBinders扩展方法通过扫描程序集来添加模型绑定的,所以您需要指定IModelBuilder注册的目标类是什么类型。 [ModelBinderType(typeof(string))] public class StringBinder : IModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { //do implementation here } } 多行的ModelBuilderTypeAttribute实例可以添加到需要对个类型注册的类中。 3. 注入HTTP抽象类 MVC集成的Autofac模块将会为HTTP抽象类添加HTTP 请求的生命收起范围内的注册。包括依稀抽象类: , HttpContextBase , HttpRequestBase , HttpResponseBase , HttpServerUtilityBase , HttpSessionStateBase , HttpApplicationStateBase , HttpBrowserCapabilitiesBase , HttpCachePolicyBase , VirtualPathProvider 需要使用上面的抽象应该使用容器的RegisterModule方法来添加AutofacWebTypesModule builder.RegisterModule(newAutofacWebTypesModule()); 4. 注入View page 您可以通过在容器创建之前添加ViewRegistrationSource 到容器中使属性注入来使MVC页面可用。 builder.RegisterSource(newViewRegistrationSource()); 您的viewpage必须继承MVC类中用于创建,当使用Razor试图引擎时将需要继承WebViewPage类: public abstract class CustomViewPage : WebViewPage { public IDependencyDependency { get; set; } } 当使用的是webform的试图引擎时,ViewPage,ViewMasterPage和ViewUserControl类都得到相应的支持。 public abstract class CustomViewPage : ViewPage { public IDependencyDependency { get; set; } } 必须确保您实际的试图页面继承了您自定义的基类。在Razor视图引擎.cshtml中可以使用@inherits指令来实现: @inherits Example.Views.Shared.CustomViewPage 使用webform时可以做如下设置 <%@ PageLanguage="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Example.Views.Shared.CustomViewPage" %> 5. 对Filter Attribute进行属性注入 为过滤器使用属性注入必须在容器创建之前调用RegisterFilterProvider方法,并将其传到AutofacDependencyResolver ContainerBuilder builder = new ContainerBuilder(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); builder.Register(c => new Logger()).As().InstancePerHttpRequest(); builder.RegisterFilterProvider(); IContainer container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 然后您就可以为您的过滤器添加属性了,并且 public class CustomActionFilter : ActionFilterAttribute { public ILogger Logger { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { Logger.Log("OnActionExecuting"); } } 下面是类似用户验证过滤器的自定义特性 public class CustomAuthorizeAttribute : AuthorizeAttribute { public ILogger Logger { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext) { Logger.Log("AuthorizeCore"); return true; } } 应用如下: [CustomActionFilter] [CustomAuthorizeAttribute] public ActionResult Index() { // ... } 关于Autofac更多的信息,可以参考autofac在google code上的wiki文档: NopCommerce是如何使用Autofac实现依赖注入的, NopCommerce将所有和Autofac注入相关的工作都放到了EngineContext中,在Global.asax的Application_Start函数 的第一句代码即是: //initialize engine context EngineContext.Initialize(false); 从这里开始EngineContext的初始化工作,初始化时会创建一个新的NopEngine,参数false指定当NopEngine不为空时 是否重新生成一个新的NopEngine。 [MethodImpl(MethodImplOptions.Synchronized)] public static IEngine Initialize(bool forceRecreate) { if (Singleton.Instance == null || forceRecreate) { var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; Debug.WriteLine("Constructing engine " + DateTime.Now); Singleton.Instance = CreateEngineInstance(config); Debug.WriteLine("Initializing engine " + DateTime.Now); Singleton.Instance.Initialize(config); } return Singleton.Instance; } NopEngine 使用单例模式,在整个程序运行期间存在一个实例,代码首先会判断NopEngine是否为空,为空的话则根据web.config中配置的 NopConfig节点信息创建一个新的NopEngine实例,然后对该实例进行初始化操作。web.config中的配置信息如下:
CreateEngineInstance函数中使用new NopEngine()创建了一个NopEngine实例,在NopEngine的构造函数处对Autofac的容器(Container)作了初始化,如下代码: public NopEngine(EventBroker broker, ContainerConfigurer configurer) { var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; InitializeContainer(configurer, broker, config); } private void InitializeContainer(ContainerConfigurer configurer, EventBroker broker, NopConfig config) { var builder = new ContainerBuilder(); _containerManager = new ContainerManager(builder.Build()); configurer.Configure(this, _containerManager, broker, config); } NopCommerce通过ContainerManager对容器做了一层封装,方便对其他类型的IOC框架的扩充和支持。Configure函数完成了所有依赖的注入,同时查找所有实现了IDependencyRegistrar接 口的类,并调用其Register方法,注册内容包括Http context、web helper、controller、data layer、plugin、cache manager、work context、services、settings、event consumers等等。 关于ContainerManager/ContainerConfigurer和IDependencyRegistrar是实现IOC的关键,下面对这两个部分做详细的讨论。 // todo:仍需继续分析具体实现 ContainerManager/ContainerConfigurer ContainerManagerContainerManager对依赖注入中使用的容器做了一层封装,提供了这些函数: , AddComponent/AddComponentInstance/AddComponentWithParameters , Resolve/ResolveAll/ResovleUnregistered , UpdateContainer DependencyRegistrar , web helper , controller , data layer , plugin , cache manager , work context , services , settings , event consumers nopCommerce中cache分析 入口: /// /// Gets a category /// /// Category identifier /// Category public virtual Category GetCategoryById(int categoryId) { if (categoryId == 0) return null; string key = string.Format(CATEGORIES_BY_ID_KEY, categoryId); return _cacheManager.Get(key, () => { return _categoryRepository.GetById(categoryId); }); } //"CurrentCategoryId" property of "CategoryNavigationModel" object depends on the current category or product. //We need to clone the cached model (the updated one should not be cached) var model = (CategoryNavigationModel)cachedModel.Clone(); model.CurrentCategoryId = activeCategoryId; return PartialView(model); } 功能描述: 1,生成cacheKey 2, 调用_cacheManager获取数据 接下来,看看_cacheManager Get方法是如何定义的。 _cacheManager.Get定义 /// /// Extensions /// public static class CacheExtensions { public static T Get(this ICacheManager cacheManager, string key, Func acquire) { return Get(cacheManager, key, 60, acquire); } public static T Get(this ICacheManager cacheManager, string key, int cacheTime, Func acquire) { if (cacheManager.IsSet(key)) { return cacheManager.Get(key); } else { var result = acquire(); //if (result != null) cacheManager.Set(key, result, cacheTime); return result; } } } 由定义可知,当从缓存存中取不到数据时,则执行匿名函数acquire获取数据。 获取数据成功后,设置缓存,这样下次就可以直接从缓存中获取数据了。 Get方法中调用的是ICacheManager接口的方法,因此需要看看是哪个类实现了该接口。方法是通过查找IoC注册,找 到实现ICacheManager接口的类。 接口注册 //cache manager builder.RegisterType().As().Named("nop_cache_static").SingleIns tance(); builder.RegisterType().As().Named("nop_cache_per_request").InstancePerHttpRequest(); 功能描述:在IoC容器中把MemoryCacheManager注册为ICacheManager,并且命名为nop_cache_static。 public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder) { //we cache presentation models between requests builder.RegisterType() .WithParameter(ResolvedParameter.ForNamed("nop_cache_static")); builder.RegisterType() .WithParameter(ResolvedParameter.ForNamed("nop_cache_static")); …… 功能描述:在IoC容器中注册CatalogController,并把它的缓存策略设置为nop_cache_static。 完成这两步注册后,在“入口”代码块中,当需要_cacheManager实例时,IoC容器将返回MemoryCacheManager的实例。 最后看看MemoryCacheManager具体是如何缓存的。 MemoryCacheManager实现 public partial class MemoryCacheManager : ICacheManager { protected ObjectCache Cache { get {return MemoryCache.Default;} } public void Set(string key, object data, int cacheTime) { if (data == null) return; var policy = new CacheItemPolicy(); policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); Cache.Add(new CacheItem(key, data), policy); } …… 由上可知,它采用的是MemoryCache来做缓存的。 Nopcommerce 的数据校验 数据校验是一个用以保证程序操作干净,正确和有用数据的流程。很多.NET程序猿使用Data Annotation Validators,不过nopCommerce用的是Fluent Validation, 一个有着文艺青年般的接口和lambda表达式构成的.NET的小型验证库,用以生成符合你业务需求的校验规则 。在nopCommerce中你必须要通过2步来添加一个校验到一些模型中: 1.创建一个继承自AbstractValidator的类并把所有必须的验 证逻辑都放入其中,看下边这些应该有所启发: public class AddressValidator : AbstractValidator { public AddressValidator(ILocalizationService localizationService, AddressSettings addressSettings) { RuleFor(x => x.FirstName) .NotEmpty() .WithMessage(localizationService.GetResource("Address.Fields.FirstName.Required")); RuleFor(x => x.LastName) .NotEmpty() .WithMessage(localizationService.GetResource("Address.Fields.LastName.Required")); RuleFor(x => x.Email) .NotEmpty() .WithMessage(localizationService.GetResource("Address.Fields.Email.Required")); 2.给你的模型类加上ValidatorAttribute属性,比如下边代码: [Validator(typeof(AddressValidator))] public partial class AddressModel : BaseNopEntityModel { 当一个视图模型被提交到控制器,ASP.NET会执行相应的校验。 Nopcommerce 事件暴露和处理 事件是把消息广播给感兴趣的部分。事件是由数据驱动的如添加,更新和删除数据。NopCommerce允许程序员“监听”他们感兴趣的事件。程序员要想玩转事件基本上有如下两条路走,一个程序员要么发布某个事件让其它人来用,要么用别的程序员编好并发布的事件。 1. 程序员为了发布一个事件,必须先取得一个IEventPublisher实例再使用相应的数据一起调用Publish方法。 2. 程序员要监听一个事件,他必须实现一个新的IConsumer泛型接口,一旦有人使用这个事件,nopCommerce会用 反射来寻找并注册这个事件的实现。
本文档为【nopcommerce 系统开发文档】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_477730
暂无简介~
格式:doc
大小:99KB
软件:Word
页数:35
分类:企业经营
上传时间:2018-04-28
浏览量:81