——In computing, a plug-in (or plugin) is a set of software components that add specific abilities to a larger software application (Wikipedia).html
Plugin,即插件,用來作NopCommerce的功能擴展。NopCommerce源碼自己提供了一些插件供參考使用。本篇文章經過閱讀官方文檔進行實踐總結,主要講解如何編寫一個數據持久化的NopCommerce插件。sql
當打開前臺產品詳細時,系統會自動追蹤並記錄用戶訪問的產品信息、訪問的用戶信息及其IP地址信息等。數據庫
1.新建項目,項目名爲Nop.Plugin.Other.ProductViewTracker,注意項目位置需統一放置在..\plugins\下,與輸出位置保持統一。app
2.添加Description.txt文件,該文件爲必備文件,且格式需保持統一。該文件描述插件相關信息,並做爲系統識別插件依據。ide
3.添加必要的文件夾,名稱可根據我的習慣,這裏爲保持命名規則統一,命名以下Controllers、Data、Domain、Services。ui
4.添加dll引用,且設置屬性"Copy Local"爲False。本項目引用如圖所示。this
5.添加實體類及其數據庫映射,添加數據庫訪問上下文。spa
5.1 實體類TrackingRecord,繼承基類BaseEntity插件
1 using Nop.Core; 2 3 namespace Nop.Plugin.Other.ProductViewTracker.Domain 4 { 5 public class TrackingRecord : BaseEntity 6 { 7 public virtual int ProductId { get; set; } 8 public virtual string ProductName { get; set; } 9 public virtual int CustomerId { get; set; } 10 public virtual string IpAddress { get; set; } 11 public virtual bool IsRegistered { get; set; } 12 } 13 }
5.2 數據庫表映射類TrackingRecordMap,繼承EntityTypeConfiguration<T>3d
1 using Nop.Plugin.Other.ProductViewTracker.Domain; 2 using System.Data.Entity.ModelConfiguration; 3 4 namespace Nop.Plugin.Other.ProductViewTracker.Data 5 { 6 public class TrackingRecordMap : EntityTypeConfiguration<TrackingRecord> 7 { 8 public TrackingRecordMap() 9 { 10 ToTable("ProductViewTracking"); 11 12 HasKey(m => m.Id); 13 Property(m => m.ProductId); 14 Property(m => m.ProductName).HasMaxLength(400); 15 Property(m => m.IpAddress); 16 Property(m => m.CustomerId); 17 Property(m => m.IsRegistered); 18 } 19 } 20 }
5.3 數據庫訪問上下文,安裝插件時生成並執行建表腳本,卸載插件時刪除該表
1 using Nop.Data; 2 using System; 3 using System.Collections.Generic; 4 using System.Data.Entity; 5 using Nop.Core; 6 using System.Data.Entity.Infrastructure; 7 8 namespace Nop.Plugin.Other.ProductViewTracker.Data 9 { 10 public class TrackingRecordObjectContext : DbContext, IDbContext 11 { 12 public TrackingRecordObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { } 13 14 #region Implementation of IDbContext 15 16 public bool AutoDetectChangesEnabled 17 { 18 get 19 { 20 throw new NotImplementedException(); 21 } 22 23 set 24 { 25 throw new NotImplementedException(); 26 } 27 } 28 29 public bool ProxyCreationEnabled 30 { 31 get 32 { 33 throw new NotImplementedException(); 34 } 35 36 set 37 { 38 throw new NotImplementedException(); 39 } 40 } 41 42 public void Detach(object entity) 43 { 44 throw new NotImplementedException(); 45 } 46 47 public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = default(int?), params object[] parameters) 48 { 49 throw new NotImplementedException(); 50 } 51 52 public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new() 53 { 54 throw new NotImplementedException(); 55 } 56 57 public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters) 58 { 59 throw new NotImplementedException(); 60 } 61 62 public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity 63 { 64 return base.Set<TEntity>(); 65 } 66 67 protected override void OnModelCreating(DbModelBuilder modelBuilder) 68 { 69 modelBuilder.Configurations.Add(new TrackingRecordMap()); 70 71 base.OnModelCreating(modelBuilder); 72 } 73 74 #endregion 75 76 public string CreateDatabaseInstallationScript() 77 { 78 return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript(); 79 } 80 81 public void Install() 82 { 83 Database.SetInitializer<TrackingRecordObjectContext>(null); 84 85 Database.ExecuteSqlCommand(CreateDatabaseInstallationScript()); 86 SaveChanges(); 87 } 88 89 public void Uninstall() 90 { 91 var dbScript = "DROP TABLE ProductViewTracking"; 92 Database.ExecuteSqlCommand(dbScript); 93 SaveChanges(); 94 } 95 } 96 }
6.添加業務服務類。
6.1 服務接口
1 using Nop.Plugin.Other.ProductViewTracker.Domain; 2 3 namespace Nop.Plugin.Other.ProductViewTracker.Services 4 { 5 public interface IViewTrackingService 6 { 7 /// <summary> 8 /// Logs the specified record. 9 /// </summary> 10 /// <param name="record">The record.</param> 11 void Log(TrackingRecord record); 12 } 13 }
6.2 業務服務類
1 using Nop.Core.Data; 2 using Nop.Plugin.Other.ProductViewTracker.Domain; 3 4 namespace Nop.Plugin.Other.ProductViewTracker.Services 5 { 6 public class ViewTrackingService : IViewTrackingService 7 { 8 private readonly IRepository<TrackingRecord> _trackingRecordRepository; 9 10 public ViewTrackingService(IRepository<TrackingRecord> trackingRecordRepository) 11 { 12 _trackingRecordRepository = trackingRecordRepository; 13 } 14 15 public void Log(TrackingRecord record) 16 { 17 _trackingRecordRepository.Insert(record); 18 } 19 } 20 }
7.實現依賴注入的註冊接口。
1 using Nop.Core.Infrastructure.DependencyManagement; 2 using Autofac; 3 using Nop.Core.Configuration; 4 using Nop.Core.Infrastructure; 5 using Nop.Plugin.Other.ProductViewTracker.Services; 6 using Nop.Web.Framework.Mvc; 7 using Nop.Plugin.Other.ProductViewTracker.Data; 8 using Nop.Data; 9 using Nop.Plugin.Other.ProductViewTracker.Domain; 10 using Nop.Core.Data; 11 using Autofac.Core; 12 13 namespace Nop.Plugin.Other.ProductViewTracker 14 { 15 public class DependencyRegistrar : IDependencyRegistrar 16 { 17 private const string CONTEXT_NAME = "nop_object_context_product_view_tracker"; 18 19 public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) 20 { 21 builder.RegisterType<ViewTrackingService>().As<IViewTrackingService>().InstancePerLifetimeScope(); 22 23 //data context 24 this.RegisterPluginDataContext<TrackingRecordObjectContext>(builder, CONTEXT_NAME); 25 26 //override required repository with our custom context 27 builder.RegisterType<EfRepository<TrackingRecord>>() 28 .As<IRepository<TrackingRecord>>() 29 .WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME)) 30 .InstancePerLifetimeScope(); 31 } 32 33 public int Order 34 { 35 get { return 1; } 36 } 37 } 38 }
8.添加MVC Controller。
1 using Nop.Core; 2 using Nop.Core.Domain.Catalog; 3 using Nop.Core.Domain.Customers; 4 using Nop.Core.Plugins; 5 using Nop.Plugin.Other.ProductViewTracker.Domain; 6 using Nop.Plugin.Other.ProductViewTracker.Services; 7 using Nop.Services.Catalog; 8 using Nop.Web.Framework.Controllers; 9 using System.Web.Mvc; 10 11 namespace Nop.Plugin.Other.ProductViewTracker.Controllers 12 { 13 public class TrackingController : BasePluginController 14 { 15 private readonly IProductService _productService; 16 private readonly IViewTrackingService _viewTrackingService; 17 private readonly IWorkContext _workContext; 18 19 public TrackingController(IWorkContext workContext, 20 IViewTrackingService viewTrackingService, 21 IProductService productService, 22 IPluginFinder pluginFinder) 23 { 24 _workContext = workContext; 25 _viewTrackingService = viewTrackingService; 26 _productService = productService; 27 } 28 29 [ChildActionOnly] 30 public ActionResult Index(int productId) 31 { 32 //Read from the product service 33 Product productById = _productService.GetProductById(productId); 34 35 //If the product exists we will log it 36 if (productById != null) 37 { 38 //Setup the product to save 39 var record = new TrackingRecord(); 40 record.ProductId = productId; 41 record.ProductName = productById.Name; 42 record.CustomerId = _workContext.CurrentCustomer.Id; 43 record.IpAddress = _workContext.CurrentCustomer.LastIpAddress; 44 record.IsRegistered = _workContext.CurrentCustomer.IsRegistered(); 45 46 //Map the values we're interested in to our new entity 47 _viewTrackingService.Log(record); 48 } 49 50 //Return the view, it doesn't need a model 51 return Content(""); 52 } 53 } 54 }
9.實現路由接口。
1 using Nop.Web.Framework.Mvc.Routes; 2 using System.Web.Mvc; 3 using System.Web.Routing; 4 5 namespace Nop.Plugin.Other.ProductViewTracker 6 { 7 public class RouteProvider : IRouteProvider 8 { 9 public int Priority 10 { 11 get 12 { 13 return 0; 14 } 15 } 16 17 public void RegisterRoutes(RouteCollection routes) 18 { 19 routes.MapRoute("Nop.Plugin.Other.ProductViewTracker.Log", 20 "tracking/productviews/{productId}", 21 new { controller = "Tracking", action = "Index" }, 22 new[] { "Nop.Plugin.Other.ProductViewTracker.Controllers" } 23 ); 24 } 25 } 26 }
10.添加插件類,繼承插件基類,重載安裝和卸載方法。
1 using Nop.Core.Plugins; 2 using Nop.Plugin.Other.ProductViewTracker.Data; 3 4 namespace Nop.Plugin.Other.ProductViewTracker 5 { 6 public class ProductViewTrackerPlugin : BasePlugin 7 { 8 private readonly TrackingRecordObjectContext _context; 9 10 public ProductViewTrackerPlugin(TrackingRecordObjectContext context) 11 { 12 _context = context; 13 } 14 15 public override void Install() 16 { 17 _context.Install(); 18 base.Install(); 19 } 20 21 public override void Uninstall() 22 { 23 _context.Uninstall(); 24 base.Uninstall(); 25 } 26 } 27 }
11.在NopCommerce的前臺,加入插件的應用代碼。這裏,咱們在頁面Nop.Web\Views\Product\ProductTemplate.Simple.cshtml中插入應用代碼。
@Html.Action("Index", "Tracking", new { productId = Model.Id })
如圖