以下列出的是程序猿攻城湿在开发nopCommerce的时候经常提出的问题。它们也表现出nopCommerce团队对一些架构的选择。
文章说明:文章来源自我的博客,于原文之理解而翻译,并非出版社那种按字词翻译风格。欢迎各位提出意见,也欢迎各位转载不过务必注明本文原址。更多 nopcommerce的文章请关注http://www.dingsea.com/?tag=nopcommerce,或者加入我们的QQ群 101675096
有哪些要求?
NopCommerce的技术和系统要求可以在这儿找到(英文)
程序猿如何向nopCommerce项目贡献代码?
NopCommerce代码托管在codeplex Mercurial代码库,用户点此访问。借此公共代码库,用户可找到将要发布的修改和以前的设计决策。如果想知道codeplex Mercurial对版本树的支持请在这儿和这儿找更多信息。程序猿可以很容易地在我们的扩展页面上传插件和语言包并分享给他人。要上传扩展,请在浏览器中访问我的帐号,选择“Your contributions and extensions”选项卡,然后点击“Upload a new extension”按钮。
我如何报告一个缺陷?
nopCommerce使用Codeplex作为官方缺陷跟踪系统,如果发现一个缺陷,可通过在Codeplex创建一个任务来报告给nopCommerce团队。程序猿或用户也可以在我们的Bug Reports论坛版块发帖子来告知新发现的缺陷。如果你的缺陷已经被记录当然最好,正因为此,验证那些没有被记录的缺陷更为重要(比较拗口)。报告重复的缺陷会分心而且让我们在新的开发和改缺陷上时间更少。
nopCommerce的数据访问层
Nop.Data项目包含一系列的类和函数来读取和写入数据库或是其它数据存储介质。Nop.Data项目有助于将数据访问的逻辑从你的业务对象中 分离出来。NopCommerce使用Entity Framework (EF) Code-First,Code-First允许程序员在源代码中定义实体(所有核心实体都在Nop.Core项目中定义),然后使用EF来生成基于C# 类的数据库,这就是为何被称为Code-First。你可以用LINQ来查询你的对象,它会悄悄地把代码转化为SQL语句并在数据库执行。 Nopcommerce有流利的API用于完全定制化的持久映射。如果想了解更多Code-First请访问这儿和这儿
控制反转和依赖注入
控制反转和依赖注入是两个密不可分的方法用来分离你应用程序中的依赖性。控制反转Inversion of Control (IoC) 意味着一个对象不会新创建一个对象并依赖着它来完成工作。相反,它们从外部获取它们想要的对象。依赖注入Dependency Injection (DI) 意味着在没有对象的干预下,一般通过能传入构造参数和一系列属性的框架组件完成。马丁虎老二(Martin Fowler)写过一篇关于依赖注入和控制反转的牛B文章,我就不要再抄到这儿了,你可以在这儿找到。NopCommerce使用Autofac类库作为IOC容器。只要你写了一个服务和此服务已实现的适当接口,你应该在任何实现了IDependencyRegistrar接口(Nop.Core.Infrastructure.DependencyManagement 命名空间).的类里注册它。比如所有nopCommerce的核心服务都在Nop.Web.Framework类库的DependencyRegistrar类中已注册。
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
builder.Register(c => c.Resolve().Request) .As() .InstancePerHttpRequest(); builder.Register(c => c.Resolve().Response) .As() .InstancePerHttpRequest();
你想创建多少依赖注册类都可以。每一个类实现了IDependencyRegistrar接口的类都有一个Order属性,可以用它来替换一个现有的依赖。要覆盖nopcommerce的依赖,设置order属性为大于0。Nopcommerce会对依赖排序,并按顺序运行,数字越大你的对象越迟被注册。
我如何注册新的路由(路由?我觉得还是用routes比较好呢?)
ASP.NET路由主要用于接受进来的浏览器请求并把它映射到具体的MVC控制器action上。在此有更多信息。Nopcommerce有一个叫IRouteProvider的接口,用于在应用程序开始时注册路由。所有的核心路由都在Nop.Web项目中的RouteProvider注册。
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
//home page
routes.MapLocalizedRoute(“HomePage”,
“”,
new { controller = “Home”, action = “Index”},
new[] { “Nop.Web.Controllers” });
你想创建多少RouteProvider都可以。比如,如果你的插件有定制路由,需要注册,于是你可以创建一个实现IRouteProvider接口的新类,再根据插件具体注册路由。
数据校验
数据校验是一个用以保证程序操作干净,正确和有用数据的流程。很多.NET程序猿使用Data Annotation Validators,不过nopCommerce用的是Fluent Validation, 一个有着文艺青年般的接口和lambda表达式构成的.NET的小型验证库,用以生成符合你业务需求的校验规则 。在nopCommerce中你必须要通过2步来添加一个校验到一些模型中:1.创建一个继承自AbstractValidator的类并把所有必须的验 证逻辑都放入其中,看下边这些应该有所启发:
public class AddressValidator : AbstractValidator { public AddressValidator(ILocalizationService localizationService) { RuleFor(x => x.FirstName) .NotEmpty() .WithMessage(localizationService.GetResource(“Address.Fields.FirstName.Required”)) .When(x => !x.FirstNameDisabled);
2.给你的模型类加上ValidatorAttribute属性,比如下边代码:
[Validator(typeof(AddressValidator))]
public class AddressModel : BaseNopEntityModel
{
当一个视图模型被提交到控制器,ASP.NET会执行相应的校验。
计划任务
有了计划任务,你可以安排一个任务在指定的时期里,在后台运行。比如nopCommerce会定时地发送队列中的email。任务是在ASP.NET线程池中由单独的线程执行。创建一个新的任务有如下基本步骤:
- 定义一个有ITask接口的类,它只有一个无参数的方法:Execute。在任务要执行的时候,这个方法会被调用,你懂的。
- 为了设定一个计划任务,程序员必须在数据库相应的表中添加一个ScheduleTask记录。你可以使用IScheduleTaskService来添加记录
事件暴露和处理
事件是把消息广播给感兴趣的部分。事件是由数据驱动的如添加,更新和删除数据。NopCommerce允许程序员“监听”他们感兴趣的事件。程序员要想玩转事件基本上有如下两条路走,一个程序员要么发布某个事件让其它人来用,要么用别的程序员编好并发布的事件。
- 程序员为了发布一个事件,必须先取得一个IEventPublisher实例再使用相应的数据一起调用Publish方法。
- 程序员要监听一个事件,他必须实现一个新的IConsumer泛型接口,一旦有人使用这个事件,nopCommerce会用反射来寻找并注册这个事件的实现。
设置API
正如其它网站平台,nopCommerce也有例如“网店名称”或“启用单页购买”这类设置,在nopCommerce中有两种办法来管理设置。
你可以用实现ISettingService接口的方法SetSetting和GetSettingByKey来加载和保存单个设置。而在nopCommerce中最牛B的处理设置的办法是创建一个ISettingService接口的新实现。每个设置将变为C#属性,程序员在需要时应该使用setting类构建函数注入设置。以下是setting类的示例代码。
/* 小结:本文是按老丁于原文之理解而翻译,并非出版社那种按字词翻译风格。欢迎各位提出意见,也欢迎各位转载不过务必注明本文原址。更多 nopcommerce的文章请关注http://www.dingsea.com/?tag=nopcommerce,或者加入我们的QQ群 101675096讨论。
*/
public class MediaSettings : ISettings
{
public int AvatarPictureSize { get; set; }
public int ProductThumbPictureSize { get; set; }
public int ProductDetailsPictureSize { get; set; }
public int ProductThumbPictureSizeOnProductDetailsPage { get; set; }
public int ProductVariantPictureSize { get; set; }
public int CategoryThumbPictureSize { get; set; }
public int ManufacturerThumbPictureSize { get; set; }
public int CartThumbPictureSize { get; set; }
public bool DefaultPictureZoomEnabled { get; set; }
public int MaximumImageSize { get; set; }
}