在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
概述在日常开发中, 我们经常谈起模块化/插件化架构,这样可既可以提高开效率,又可以实现良好的扩展性,尤其对于产品化的系统有更好的实用性。 架构我们采用的是MVC5(本文中介绍的方法对于MVC4也是适用的),如下图,解决方案中有四个项目,其中 WeDiscuss 为前端,WeDiscuss.Plugin.Framework 为插件公共类库 WeDiscuss.Plugin.Album 为插件(相册) WeDiscuss.Plugin.News 为插件(新闻),本文只是讲解决插件的实现方式,就不多做其它如果业务逻辑、数据访问层等 注;每个插件都有自已的(M、V、C),内部实现和常用MVC没有区别,这样可以方便的开发,没有其它新知识的引入。
其中,插件层可以在主项目中引用,也可以不引用,或是放到其它目录下(如把插件DLL单独放到“Plugins”目录中),如果不引用就采用在编译完成时复制 下面讲解编译完成复制方法,如想复制到“Plugins”目录中请修改BIN为“Plugins”:在如下图加入: copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)Wediscuss\Bin\" 如何让ASP.NET加载BIN目录之外的路径的Assembly我们把各个模块编译出来的assembly和各个模块的配置文件自动放到一个bin平级的plugin目录,然后web应用启动的时候自动扫描这个plugin目录并加载各个模块plugin,这个怎么做到的?大家也许知道,ASP.NET只允许读取Bin目录下的assbmely,不可以读取其他路径,包括Bin\abc等,即使在web.config这样配置probing也不行:(不信你可以试一下)
<configuration> Element <runtime> Element <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="bin;plugins;"/> </assemblyBinding> </runtime> </configuration> 如何注入菜单插件能用了,但也想动态注入菜单,这样才实现了自动化,要不还是人工进行菜单注入永远是半自动化,这和我们开发的思想是不想符的,下面就来说一下菜单的注入 1、首称在WeDiscuss.Plugin.Framework 为插件公共类库中建实体类PluginMenu 和PluginMenus /* * ------------------------------------------------------------------------------- * 功能描述: * * 创建人: JunHan(俊涵) * 创建日期: 2013/12/15 21:59:16 * 创建说明: * * 修改人: * 修改日期: * 修改说明: * * ------------------------------------------------------------------------------- */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Mvc; namespace WeDiscuss.Plugin.Framework { public class PluginMenus { public List<PluginMenu> MenuList { get; set; } public string CssClass { get; set; } public int MenuType { get; set; } public string Html { get { StringBuilder stringBuilder = new StringBuilder(); foreach (var menu in MenuList) { TagBuilder tagBuilder = new TagBuilder("a"); tagBuilder.MergeAttribute("href", menu.MenuUrl); tagBuilder.InnerHtml = menu.MenuText; tagBuilder.MergeAttribute("class", CssClass); stringBuilder.Append(tagBuilder.ToString(TagRenderMode.Normal) + "\r\n"); } return stringBuilder.ToString(); } } public List<PluginMenu> AvailableList { get { if (MenuList == null) { return new List<PluginMenu>(); } if (MenuType == 0) { return MenuList; } if (!MenuList.Any(o => o.MenuType == MenuType)) { return new List<PluginMenu>(); } return MenuList.Where(o => o.MenuType == MenuType).ToList(); } } } public class PluginMenu { public string MenuText { get; set; } public string MenuUrl { get; set; } public int MenuType { get; set; } public int MenuOrder { get; set; } public bool Visible { get; set; } } } 这样我们就实现了菜单的结构,接下来就是采单的生成或注入方法: 新建 AppPlugin 和 PluginApplication来实现菜单的初使化方法,并将生成好的菜单存放在静态变量中。 /* * ------------------------------------------------------------------------------- * 功能描述: * * 创建人: JunHan(俊涵) * 创建日期: 2013/12/15 23:29:58 * 创建说明: * * 修改人: * 修改日期: * 修改说明: * * ------------------------------------------------------------------------------- */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WeDiscuss.Plugin.Framework { public class PluginApplication : BaseMvcPluginApplication { #region Plugin Menu Support public static PluginMenus PluginMenus = new PluginMenus(); public void RegisterMenuItem(PluginMenu menu) { lock (PluginMenus) { if (PluginMenus.MenuList == null) PluginMenus.MenuList = new List<PluginMenu>(); PluginMenus.MenuList.Add(new PluginMenu() { MenuText = menu.MenuText, MenuUrl = menu.MenuUrl, MenuType = menu.MenuType, MenuOrder = menu.MenuOrder }); PluginMenus.MenuList = PluginMenus.MenuList.OrderBy(o => o.MenuOrder).ToList(); } } #endregion public static new PluginApplication Instance { get { return BaseMvcPluginApplication.Instance as PluginApplication; } set { BaseMvcPluginApplication.Instance = value; } } protected override bool ShouldIncludeResourceCore(BaseMvcPluginApplication.ResourceTypes type, IMvcPlugin plugin) { return ShouldIncludeResource(plugin, null); } protected virtual bool ShouldIncludeResource(IMvcPlugin plugin, object resource) { bool should = true; if (plugin != null) { if ((should = plugin.Enabled) && plugin is AppPlugin) should = ((AppPlugin)plugin).ShouldIncludeResource(resource); } return should; } protected override void AddAdditionalRazorViewLocationsCore(List<string> lst) { lst.Add("~/Plugins/PluginDemo/Views.{1}.{0}.cshtml"); } public static PluginApplication SetupApplication(object bundles, object routes) { PluginApplication me = new PluginApplication(bundles, routes); return me; } protected PluginApplication(object bundles, object routes) : base(bundles, routes) { } } public class AppPlugin : BaseMvcPlugin { public PluginApplication _App { get { return (PluginApplication)App; } } public AppPlugin(bool ensureStandardViewLocation = true) : base(ensureStandardViewLocation) { } public void DefineMenuItem(PluginMenu item) { _App.RegisterMenuItem(item); } public virtual bool ShouldIncludeResource(object content) { return true; } } } 2、菜单初使化 在每个插件项目中新建一类,并继承AppPlugin,重写方法:SetupExtensions 调用DefineMenuItem 实现菜单初使化,在菜单的结果中我们看到有MenuType类型,这里我们自定义,一般会用枚举来实现,可以定义为前台或后台等,一个插件可以拥有多个菜单,可以注入多个地方
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Text; using System.Threading.Tasks; using WeDiscuss.Plugin.Framework; namespace WeDiscuss.Plugin.Album { [Export(typeof(IMvcPlugin))] [MvcPluginMetadata("AlbumPlugin", null, "Demo App Site Album", "" 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论