我的NopCommerce之旅(9): 编写Plugin实例
一、基础介绍
——In computing, a plug-in (or plugin) is a set of software components that add specific abilities to a larger software application (Wikipedia).
Plugin,即插件,用来做NopCommerce的功能扩展。NopCommerce源码本身提供了一些插件供参考使用。本篇文章通过阅读官方文档进行实践总结,主要讲解如何编写一个数据持久化的NopCommerce插件。
二、效果展示
当打开前台产品详细时,系统会自动追踪并记录用户访问的产品信息、访问的用户信息及其IP地址信息等。

三、编写步骤
1.新建项目,项目名为Nop.Plugin.Other.ProductViewTracker,注意项目位置需统一放置在..\plugins\下,与输出位置保持统一。

2.添加Description.txt文件,该文件为必备文件,且格式需保持统一。该文件描述插件相关信息,并作为系统识别插件依据。

3.添加必要的文件夹,名称可根据个人习惯,这里为保持命名规则统一,命名如下Controllers、Data、Domain、Services。

4.添加dll引用,且设置属性"Copy Local"为False。本项目引用如图所示。

5.添加实体类及其数据库映射,添加数据库访问上下文。
5.1 实体类TrackingRecord,继承基类BaseEntity
using Nop.Core; namespace Nop.Plugin.Other.ProductViewTracker.Domain
{
public class TrackingRecord : BaseEntity
{
public virtual int ProductId { get; set; }
public virtual string ProductName { get; set; }
public virtual int CustomerId { get; set; }
public virtual string IpAddress { get; set; }
public virtual bool IsRegistered { get; set; }
}
}
5.2 数据库表映射类TrackingRecordMap,继承EntityTypeConfiguration<T>
using Nop.Plugin.Other.ProductViewTracker.Domain;
using System.Data.Entity.ModelConfiguration; namespace Nop.Plugin.Other.ProductViewTracker.Data
{
public class TrackingRecordMap : EntityTypeConfiguration<TrackingRecord>
{
public TrackingRecordMap()
{
ToTable("ProductViewTracking"); HasKey(m => m.Id);
Property(m => m.ProductId);
Property(m => m.ProductName).HasMaxLength();
Property(m => m.IpAddress);
Property(m => m.CustomerId);
Property(m => m.IsRegistered);
}
}
}
5.3 数据库访问上下文,安装插件时生成并执行建表脚本,卸载插件时删除该表
using Nop.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using Nop.Core;
using System.Data.Entity.Infrastructure; namespace Nop.Plugin.Other.ProductViewTracker.Data
{
public class TrackingRecordObjectContext : DbContext, IDbContext
{
public TrackingRecordObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { } #region Implementation of IDbContext public bool AutoDetectChangesEnabled
{
get
{
throw new NotImplementedException();
} set
{
throw new NotImplementedException();
}
} public bool ProxyCreationEnabled
{
get
{
throw new NotImplementedException();
} set
{
throw new NotImplementedException();
}
} public void Detach(object entity)
{
throw new NotImplementedException();
} public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = default(int?), params object[] parameters)
{
throw new NotImplementedException();
} public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
throw new NotImplementedException();
} public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
throw new NotImplementedException();
} public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TrackingRecordMap()); base.OnModelCreating(modelBuilder);
} #endregion public string CreateDatabaseInstallationScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
} public void Install()
{
Database.SetInitializer<TrackingRecordObjectContext>(null); Database.ExecuteSqlCommand(CreateDatabaseInstallationScript());
SaveChanges();
} public void Uninstall()
{
var dbScript = "DROP TABLE ProductViewTracking";
Database.ExecuteSqlCommand(dbScript);
SaveChanges();
}
}
}
6.添加业务服务类。
6.1 服务接口
using Nop.Plugin.Other.ProductViewTracker.Domain; namespace Nop.Plugin.Other.ProductViewTracker.Services
{
public interface IViewTrackingService
{
/// <summary>
/// Logs the specified record.
/// </summary>
/// <param name="record">The record.</param>
void Log(TrackingRecord record);
}
}
6.2 业务服务类
using Nop.Core.Data;
using Nop.Plugin.Other.ProductViewTracker.Domain; namespace Nop.Plugin.Other.ProductViewTracker.Services
{
public class ViewTrackingService : IViewTrackingService
{
private readonly IRepository<TrackingRecord> _trackingRecordRepository; public ViewTrackingService(IRepository<TrackingRecord> trackingRecordRepository)
{
_trackingRecordRepository = trackingRecordRepository;
} public void Log(TrackingRecord record)
{
_trackingRecordRepository.Insert(record);
}
}
}
7.实现依赖注入的注册接口。
using Nop.Core.Infrastructure.DependencyManagement;
using Autofac;
using Nop.Core.Configuration;
using Nop.Core.Infrastructure;
using Nop.Plugin.Other.ProductViewTracker.Services;
using Nop.Web.Framework.Mvc;
using Nop.Plugin.Other.ProductViewTracker.Data;
using Nop.Data;
using Nop.Plugin.Other.ProductViewTracker.Domain;
using Nop.Core.Data;
using Autofac.Core; namespace Nop.Plugin.Other.ProductViewTracker
{
public class DependencyRegistrar : IDependencyRegistrar
{
private const string CONTEXT_NAME = "nop_object_context_product_view_tracker"; public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
{
builder.RegisterType<ViewTrackingService>().As<IViewTrackingService>().InstancePerLifetimeScope(); //data context
this.RegisterPluginDataContext<TrackingRecordObjectContext>(builder, CONTEXT_NAME); //override required repository with our custom context
builder.RegisterType<EfRepository<TrackingRecord>>()
.As<IRepository<TrackingRecord>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME))
.InstancePerLifetimeScope();
} public int Order
{
get { return ; }
}
}
}
8.添加MVC Controller。
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Plugins;
using Nop.Plugin.Other.ProductViewTracker.Domain;
using Nop.Plugin.Other.ProductViewTracker.Services;
using Nop.Services.Catalog;
using Nop.Web.Framework.Controllers;
using System.Web.Mvc; namespace Nop.Plugin.Other.ProductViewTracker.Controllers
{
public class TrackingController : BasePluginController
{
private readonly IProductService _productService;
private readonly IViewTrackingService _viewTrackingService;
private readonly IWorkContext _workContext; public TrackingController(IWorkContext workContext,
IViewTrackingService viewTrackingService,
IProductService productService,
IPluginFinder pluginFinder)
{
_workContext = workContext;
_viewTrackingService = viewTrackingService;
_productService = productService;
} [ChildActionOnly]
public ActionResult Index(int productId)
{
//Read from the product service
Product productById = _productService.GetProductById(productId); //If the product exists we will log it
if (productById != null)
{
//Setup the product to save
var record = new TrackingRecord();
record.ProductId = productId;
record.ProductName = productById.Name;
record.CustomerId = _workContext.CurrentCustomer.Id;
record.IpAddress = _workContext.CurrentCustomer.LastIpAddress;
record.IsRegistered = _workContext.CurrentCustomer.IsRegistered(); //Map the values we're interested in to our new entity
_viewTrackingService.Log(record);
} //Return the view, it doesn't need a model
return Content("");
}
}
}
9.实现路由接口。
using Nop.Web.Framework.Mvc.Routes;
using System.Web.Mvc;
using System.Web.Routing; namespace Nop.Plugin.Other.ProductViewTracker
{
public class RouteProvider : IRouteProvider
{
public int Priority
{
get
{
return ;
}
} public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Nop.Plugin.Other.ProductViewTracker.Log",
"tracking/productviews/{productId}",
new { controller = "Tracking", action = "Index" },
new[] { "Nop.Plugin.Other.ProductViewTracker.Controllers" }
);
}
}
}
10.添加插件类,继承插件基类,重载安装和卸载方法。
using Nop.Core.Plugins;
using Nop.Plugin.Other.ProductViewTracker.Data; namespace Nop.Plugin.Other.ProductViewTracker
{
public class ProductViewTrackerPlugin : BasePlugin
{
private readonly TrackingRecordObjectContext _context; public ProductViewTrackerPlugin(TrackingRecordObjectContext context)
{
_context = context;
} public override void Install()
{
_context.Install();
base.Install();
} public override void Uninstall()
{
_context.Uninstall();
base.Uninstall();
}
}
}
11.在NopCommerce的前台,加入插件的应用代码。这里,我们在页面Nop.Web\Views\Product\ProductTemplate.Simple.cshtml中插入应用代码。
@Html.Action("Index", "Tracking", new { productId = Model.Id })
如图

我的NopCommerce之旅(9): 编写Plugin实例的更多相关文章
- 我的NopCommerce之旅(8): 路由分析
一.导图和基础介绍 本文主要介绍NopCommerce的路由机制,网上有一篇不错的文章,有兴趣的可以看看NopCommerce源码架构详解--对seo友好Url的路由机制实现源码分析 SEO,Sear ...
- [转]NopCommerce之旅: 应用启动
本文转自:http://www.cnblogs.com/devilsky/p/5359881.html 我的NopCommerce之旅(6): 应用启动 一.基础介绍 Global.asax 文件 ...
- 初探webpack之编写plugin
初探webpack之编写plugin webpack通过plugin机制让其使用更加灵活,以适应各种应用场景,当然也大大增加了webpack的复杂性,在webpack运行的生命周期中会广播出许多事件, ...
- 【小白的CFD之旅】13 敲门实例【续3】
接上文[小白的CFD之旅]12 敲门实例[续2] 4 Results4.1 计算监测图形4.2 Graphics4.2.1 壁面温度分布4.2.2 创建截面4.2.3 显示截面物理量4.2.4 Pat ...
- 【小白的CFD之旅】11 敲门实例【续】
主要内容: 接上文[小白的CFD之旅]10 敲门实例 2.4 Materials设置2.5 Cell Zone Conditions2.6 Boundary Conditons2.7 Dynamic ...
- [转]nopcommerce商城系统--如何编写一个插件
本文转自:http://www.cnblogs.com/ganqiyin/p/3680771.html 原址:http://www.nopcommerce.com/docs/77/how-to-wri ...
- nopcommerce商城系统--如何编写一个插件
原址:http://www.nopcommerce.com/docs/77/how-to-write-a-nopcommerce-plugin.aspx plug-in (或 plugin)是一个为更 ...
- 我的NopCommerce之旅(7): 依赖注入(IOC/DI)
一.基础介绍 依赖注入,Dependency Injection,权威解释及说明请自己查阅资料. 这里简单说一下常见使用:在mvc的controller的构造方法中定义参数,如ICountryServ ...
- 我的NopCommerce之旅(5): 缓存
一.基础介绍 1.什么是cache Web缓存是指一个Web资源(如html页面,图片,js,数据等)存在于Web服务器和客户端(浏览器)之间的副本. 2.为什么要用cache 即 ...
随机推荐
- HDOJ-2058
The sum problem Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)T ...
- 面试6-----11 const和静态变量那些事儿
6 看看const和指针的那些事儿 const在int*左边 const在int*右边 const在int*两边------>请看代码注释 (1)代码 #include <stdio.h& ...
- 2-2和2-3基本数据类型 & 2-4基本数据类型详解 & 3-1和3-2整形字面量值及变量声
2-4基本数据类型详解 3-1和3-2整形字面量值及变量声 023是八进制的 0x1357是十六进制 0X3C也是十六进制 0x1abL:长整型 变量声明 数据类型 空格 变量名 赋值: 变量的定义:
- 制作Docker镜像的两种方式
此文已由作者朱笑天授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.使用docker commit命令制作docker镜像 1. pull一个centos6.6的基础镜像, ...
- E20190212-mt
创建: 2019/02/12 reserve n. 储备; 保留; 保护区; 替补队员; vt. 储备; 保留; 预约; vi. 预订; slot n. 位置; 狭槽,水沟; [人名] ...
- PL/SQL 的 事务处理
原文连接 http://blog.csdn.net/lhl6688/article/details/42874109 BEGIN DECLARE V_COUNT INTEGER; -- 表中记录 ...
- cogs 610. 数对的个数
610. 数对的个数 ★★ 输入文件:dec.in 输出文件:dec.out 简单对比时间限制:1 s 内存限制:128 MB Description出题是一件痛苦的事情!题目看多了也 ...
- 洛谷P3265 [JLOI2015]装备购买(线性基+高斯消元)
传送门 不知道线性基是什么东西的可以看看蒟蒻的总结 不难看出题目讲的就是线性基 这种最小化权值的问题一般都是贪心的,就是按价值从低到高考虑每一个是否能选 据说贪心的证明得用拟阵我不会 据说这题是实数意 ...
- Ruby对象模型总结
参考<Ruby元编程>,元编程,即 用来编写代码的代码 . 对象由一组实例变量和一个类的引用组成 对象的方法存在与对象所属的类中,类似js中的prototype,在ruby中准确的说,应该 ...
- java面试基础问题
1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致. 2.Java有 ...