本章主要讲解如何为框架新增插件化开发功能。

在.net 4.0中,我们可以在Application开始之前,通过PreApplicationStartMethod方法加载所需要的任何东西。那么今天我们主要做的工作就集中在这个时间段:

1.将插件DLL及文件拷贝入主网站目录并编译

2.加载Plugin

首先来说说第一步,由于这步里面,我们主要拷贝DLL及文件,所以我们利用了一个List<Assembly>容器来记录所有的插件的DLL,具体代码如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Web;
   6:  using System.Reflection;
   7:  using System.Web.Hosting;
   8:  using System.IO;
   9:  using System.Web.Compilation;
  10:  using TinyFrame.Framework;
  11:   
  12:  [assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")]
  13:  namespace TinyFrame.Framework
  14:  {
  15:      public class PluginManager
  16:      {
  17:          private const string pluginDirectory = "~/Areas";
  18:          private const string pluginShadowCopyDirectory = "~/Areas/Temp";
  19:          private static readonly List<Assembly> pluginAssemblies=new List<Assembly>();
  20:   
  21:          //初始化方法
  22:          public static void Initialize()
  23:          {
  24:              var pluginPath = HostingEnvironment.MapPath(pluginDirectory);
  25:              //var pluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");
  26:              foreach (var file in Directory.EnumerateFiles(pluginPath, "*Plugin*.dll", SearchOption.AllDirectories))
  27:              {
  28:                  pluginAssemblies.Add(Assembly.LoadFile(file));
  29:              }
  30:   
  31:              pluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);
  32:   
  33:              AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
  34:          }
  35:   
  36:          private static Assembly AssemblyResolve(object sender, ResolveEventArgs resolveArgs)
  37:          {
  38:              var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
  39:              // 如果未加载程序集
  40:              foreach (var assembly in currentAssemblies)
  41:              {
  42:                  if (assembly.FullName == resolveArgs.Name || assembly.GetName().Name == resolveArgs.Name)
  43:                  {
  44:                      return assembly;
  45:                  }
  46:              }
  47:   
  48:              return null;
  49:          }
  50:   
  51:          //得到程序集名稱
  52:          public static List<string> PluginNames()
  53:          {
  54:              return pluginAssemblies.Select(c => c.GetName().Name).ToList();
  55:          }
  56:      }
  57:  }

第12行,主要标志本方法将于Application_Start开始之前执行。

第17行,主要定义了主网站中放置插件的目录,这里为Areas目录。

第22行,为我们的初始化方法。

第28行,遍历我们的插件目录,加载名称中包含Plugin关键字的DLL。

第31行,遍历完毕后,将这些DLL加入到BuildManager中,以便于编译操作。

这里讲完之后,剩下的就是来写我们的插件了,这里要进行的是第二步:

首先,新建一个TinyFrame.Plugin.NivoSlider的MVC 4 站点,在这个站点根目录下面添加一个名称为AreaRegister.cs的类文件,内容如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:  using System.Web.Mvc;
   6:   
   7:  namespace TinyFrame.Plugin.Sample
   8:  {
   9:      public class AreaRegister : AreaRegistration
  10:      {
  11:   
  12:          public override string AreaName
  13:          {
  14:              get
  15:              {
  16:                  return "TinyFrame.Plugin.NivoSlider";
  17:              }
  18:          }
  19:   
  20:          public override void RegisterArea(AreaRegistrationContext context)
  21:          {
  22:              context.MapRoute(
  23:                      "NivoSlider_default",
  24:                      "NivoSlider/{controller}/{action}/{id}",
  25:                      new { action = "Index", id = UrlParameter.Optional },
  26:                      new string[] { "TinyFrame.Plugin.NivoSlider.Controllers" }
  27:                  );
  28:          }
  29:   
  30:      }
  31:  }

其中,类本身需要继承自AreaRegistration,以便于使用MVC本身提供的插件化支持。然后通过重载AreaName和RegisterArea方法,实现AreaName的定义和路由的映射。AreaName主要是为了识别返回的Plugin内容区域,而RegisterArea主要是为路由访问提供了支持。需要注意的是,第26行是Controller的命名空间,需要写上,否则会因为插件的目录和主站的目录一致,导致报错。

然后我们来添加测试的Controller和View:

在Controllers文件夹下面添加NivoSliderController.cs类,并返回一个空的页面:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:  using System.Web.Mvc;
   6:   
   7:  namespace TinyFrame.Plugin.NivoSlider.Controllers
   8:  {
   9:      public class NivoSliderController : Controller
  10:      {
  11:          public ActionResult Index()
  12:          {
  13:              return View();
  14:          }
  15:   
  16:      }
  17:  }

然后在Views文件夹下新建NivoSlider文件夹,并在其中创建一个名称为Index.cshtml的页面文件:

   1:  @{
   2:      ViewBag.Title = "Index";
   3:  }
   4:  <div style="width:600px;height:400px;margin:0 auto;">
   5:    <div class="slider-wrapper theme-default" style="float:left;">
   6:          <div id="slider" class="nivoSlider">
   7:              <img src="@Url.Content("~/Content/slider/images/toystory.jpg")"  alt="" />
   8:              <img src="@Url.Content("~/Content/slider/images/up.jpg")"        alt="" title="This is an example of a caption" />
   9:              <img src="@Url.Content("~/Content/slider/images/walle.jpg")"     alt="" data-transition="slideInLeft" />
  10:              <img src="@Url.Content("~/Content/slider/images/nemo.jpg")"      alt="" title="#htmlcaption" />
  11:          </div>
  12:          <div id="htmlcaption" class="nivo-html-caption">
  13:              <strong>This</strong> is an example of a <em>HTML</em> caption with <a href="#">a link</a>. 
  14:          </div>
  15:      </div>
  16:  </div>
  17:   

这样,我们的页面就准备好了。

然后右击这个插件项目,选择属性,在“生成”标签页面,我们需要把输出路径从"bin\"修改成 “..\TinyFrame.Web\Areas\”,这样做是为了将最新的DLL文件拷贝到主站目录中。

之后在”生成事件“标签页面,在”后期生成事件命令行“下的文本框中,输入以下命令,以便于将样式文件等也拷贝到主站的Areas插件目录中:

   1:  xcopy "$(ProjectDir)\Views" "$(TargetDir)\TinyFrame.Plugin.NivoSlider\Views\" /s /i /y
   2:  xcopy "$(ProjectDir)\Areas" "$(TargetDir)\TinyFrame.Plugin.NivoSlider\Areas\" /s /i /y
   3:  xcopy "$(ProjectDir)\Content" "$(TargetDir)\TinyFrame.Plugin.NivoSlider\Content\" /s /i /y

其中,TinyFrame.Plugin.NivoSlider就是你之前定义的AreaName。

这样,当项目编译完毕之后,会自动把DLL文件,视图文件,样式文件等拷贝到主站的Areas插件目录下:

最后让我们来配置下主站:

首先在主站的Content目录下添加slider文件夹,并将NivoSlider用到的所有CSS文件,JS文件引入,然后利用ActionLink,来链接至这个幻灯片插件:

   1:  <!DOCTYPE html>
   2:   
   3:  <html>
   4:  <head>
   5:      @RenderSection("featured", false)
   6:      <meta name="viewport" content="width=device-width" />
   7:      <title>书籍借阅系统</title>
   8:      <!--EasyUI CSS files-->
   9:      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/jqueryeasyui/themes/default/easyui.css")" />
  10:      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/jqueryeasyui/themes/icon.css")" />
  11:   
  12:      <!--BootStrap CSS&JS files-->
  13:      <link href="@Url.Content("~/Content/mycss/default.css")" rel="stylesheet" type="text/css" />
  14:      <link href="@Url.Content("~/Content/bootstrap/css/bootstrap.min.css")" rel="stylesheet" type="text/css" />
  15:      <link href="@Url.Content("~/Content/bootstrap/css/bootstrap-theme.min.css")" rel="stylesheet" type="text/css" />
  16:      <script src="@Url.Content("~/Content/jquery/jquery.min.js")" type="text/javascript"></script>
  17:      <script src="@Url.Content("~/Content/bootstrap/js/bootstrap.min.js")" type="text/javascript"></script>
  18:   
  19:      <!--EasyUI JS files-->
  20:      <script src="@Url.Content("~/Content/jqueryeasyui/jquery.easyui.min.js")" type="text/javascript")"></script>
  21:      <script src="@Url.Content("~/Content/jqueryeasyui/plugins/datagrid-detailview.js")" type="text/javascript")"></script>
  22:      <script src="@Url.Content("~/Content/jqueryeasyui/DataGrid.js")" type="text/javascript"></script>
  23:   
  24:      <!--NivoSlider-->
  25:      <link href="@Url.Content("~/Content/slider/themes/default/default.css")" rel="stylesheet" type="text/css" />
  26:      <link href="@Url.Content("~/Content/slider/nivo-slider.css")" rel="stylesheet" type="text/css" />
  27:      <script src="@Url.Content("~/Content/slider/scripts/jquery.nivo.slider.js")" type="text/javascript"></script>
  28:      <script src="@Url.Content("~/Content/slider/scripts/base.js")" type="text/javascript"></script>
  29:  </head>
  30:  <body>
  31:      <div class="containerMaster">
  32:          <ul class="nav nav-pills" id="menu">
  33:            <li>@Html.ActionLink("书籍入库", "Book", "Home", new { Area = string.Empty }, new { })</li>
  34:            <li>@Html.ActionLink("借入借出", "BookLend", "Home", new { Area = string.Empty }, new { })</li>
  35:            <li>@Html.ActionLink("书籍分类", "BookType", "Home", new { Area = string.Empty }, new { })</li>
  36:            <li>@Html.ActionLink("书籍存放", "BookPlace", "Home", new { Area = string.Empty }, new { })</li>
  37:            <li>@Html.ActionLink("学生管理", "Student", "Home", new { Area = string.Empty }, new { })</li>
  38:            @*<li>@Html.ActionLink("人员管理","Manager","Home")</li>*@
  39:            

<li>@Html.ActionLink("幻灯片插件", "Index", "NivoSlider", new { Area = "TinyFrame.Plugin.NivoSlider" }, new { })</li>

  40:          </ul>
  41:      </div>
  42:      @RenderBody()
  43:  </body>
  44:  </html>

需要注意的是,在上图代码中,着色的部分是访问插件的链接,Area = "TinyFrame.Plugin.NivoSlider"是我们之前定义的AreaName。

运行起来项目,让我们看看结果吧:

这节就讲完了,我们用已有的方法实现了一个简易的插件系统,但是,这个插件系统还不完善,做到商用还是远远不够的,缺点有以下几个:

1.无法进行热插拔,插件的更新需要重启网站。

2.无法检测插件的更新。

而下一章节,我们将会打造一个克服以上缺点的强大易用的插件系统。敬请期待。

以下问题及解决方法是我在开发过程中遇到的,在这里我讲答案贴出,以便于记录及追踪:

1).提示无法找到System.Web.Optimization,是否缺少引用?
    解决方法:
      1.PM> Install-Package Microsoft.AspNet.Web.Optimization 或者 
      2.删掉web.config中的引用

2).“找到多个与名为“Home”的控制器匹配的类型。如果为此请求(“{controller}/{action}/{id}”)提供服务的路由在搜索匹配此请求的控制器时没有指定命名空间,则会发生此情况。如果是这样,请通过调用含有“namespaces”参数的“MapRoute”方法的重载来注册此路由。”
解决方法:
出现该问题的愿原因是在默认的Golbal.asax.cs文件中已经注册了默认路由
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
//new string[] { "Working_with_Areas.Controllers"}
);

}

而在AREAS中有注册了一个同名的Controller

public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
//new string[] { "Working_with_Areas.Areas.Admin.Controllers" }
);
}
解决方法就是在每个注册Router的时候加上命名空间即可。

3).运行的时候,经常无法出现最新编译好的页面。

调试plugin的时候,请先将 输出路径改成  bin\  等调试完毕后,修改成..\TinyFrame.Web\Areas\,否则你调试的页面一直都是Bin目录下的,而非你设置的..\TinyFrame.Web\Areas\ 目录下的。

2014.04.22更新

1.添加了一个强类型的Plugin插件,用于检测强类型程序集是否能够被编译。

2.插件路由新增了controller参数,用于规避某些特定场合下访问出现404的错误。

 public class AreaRegister : AreaRegistration
{ public override string AreaName
{
get
{
return "TinyFrame.Plugin.NivoSlider";
}
} public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"NivoSlider_default",
"NivoSlider/{controller}/{action}/{id}",
new { controller = "NivoSlider", action = "Index", id = UrlParameter.Optional },
new string[] { "TinyFrame.Plugin.NivoSlider.Controllers" }
);
} }

3.修改了生成事件中的后期生成命令行,由于插件中的Content或者是Scripts中的某些图片或者是文件需要拷贝到主站中,所以这里添加了拷贝到主站目录的方法。

最后,提供源代码下载:

百度网盘链接

微云链接

TinyFrame升级之八:实现简易插件化开发的更多相关文章

  1. Android插件化开发

    客户端开发给人的印象往往是小巧,快速奔跑.但随着产品的发展,目前产生了大量的门户型客户端.功能模块持续集成,开发人员迅速增长.不同的开发小组开发不同的功能模块,甚至还有其他客户端集成进入.能做到功能模 ...

  2. 【我的Android进阶之旅】Android插件化开发学习资料

    1.目前开源的插件开发框架大致有哪些? 1. 任玉刚 的 dynamic-load-apk Github 地址:https://github.com/singwhatiwanna/dynamic-lo ...

  3. Android 插件化开发(四):插件化实现方案

    在经过上面铺垫后,我们可以尝试整体实现一下插件化了.这里我们先介绍一下最简单的实现插件化的方案. 一.最简单的插件化实现方案 最简单的插件化实现方案,对四大组件都是适用的,技术面涉及如下: 1). 合 ...

  4. Android组件化和插件化开发

    http://www.cnblogs.com/android-blogs/p/5703355.html 什么是组件化和插件化? 组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Modul ...

  5. Android 使用动态载入框架DL进行插件化开发

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客) 概述: 随着应用的不断迭代.应用的体积不断增大,项目越来越臃肿,冗余添 ...

  6. Android插件化开发---执行未安装apk中的Service

    欢迎各位增加我的Android开发群[257053751​] 假设你还不知道什么叫插件化开发.那么你应该先读一读之前写的这篇博客:Android插件化开发,初入殿堂 上一篇博客主要从总体角度分析了一下 ...

  7. NET 平台下的插件化开发内核

    .NET 平台下的插件化开发内核(Rabbit Kernel)   每个程序猿都有一个框架梦,曾经在2013年8月15日写过一篇“Koala Framework是什么?我为什么要写这个框架?”的文章, ...

  8. JavaScript插件化开发

    大熊君JavaScript插件化开发 一,开篇分析 Hi,大家好!大熊君又和大家见面了,还记得昨天的那篇文章吗------这个系列的开篇(第一季).主要讲述了以“jQuery的方式如何开发插件”, 那 ...

  9. 插件化开发—动态加载技术加载已安装和未安装的apk

    首先引入一个概念,动态加载技术是什么?为什么要引入动态加载?它有什么好处呢?首先要明白这几个问题,我们先从 应用程序入手,大家都知道在Android App中,一个应用程序dex文件的方法数最大不能超 ...

随机推荐

  1. 项目管理之道--纪我的新书《PMP项目管理认证学习指南(第4版)》出版并预祝大卖!

    新年伊始,我最新的项目管理书籍——<PMP项目管理认证学习指南(第4版)>也出版了,真是新年新气象啊!翻译英文书籍是一件任重道远的工作,除了要具备扎实的基本功,熟悉相关的领域外,还需要细致 ...

  2. .Net Attribute详解(上)-Attribute本质以及一个简单示例

    Attribute的直接翻译是属性,这和Property容易产生混淆,所以一般翻译成特性加以区分.Attribute常常的表现形式就是[AttributeName], 随意地添加在class, met ...

  3. 使用MiniProfiler给Asp.net MVC和Entity Framework号脉(附源码)

    在学习python开发框架pylons/pyramid的过程中,里面有个非常棒的页面性能监控功能,这样在开发过程中,你能清楚的知道当前页面的性能以及其它参数. 这里介绍一下如何给Asp.net MVC ...

  4. ASP.NET MVC 监控诊断、本地化和缓存

    这篇博客主要是针对asp.net mvc项目的一些常用的东东做一个讲解,他们分别是监控诊断.本地化和缓存.虽然前两者跟asp.net mvc看上去好像是没什么关联. 但其实如果真正需要做asp.net ...

  5. ASP.NET Web API 简介

    ASP.NET MVC 4 包含了 ASP.NET Web API, 这是一个创建可以连接包括浏览器.移动设备等多种客户端的 Http 服务的新框架, ASP.NET Web API 也是构建 RES ...

  6. 国内Hadoop应用现状

    Hadoop在国内主要以互联网公司为主,下面主要介绍大规模使用Hadoop或研究Hadoop的公司. 1. 百度 百度在2006年就关注了Hadoop并开始调研和使用,截止2012年,总的集群规模超过 ...

  7. docker-6 管理工具

    Shipyard 是一个基于 Web 的 Docker 管理工具,支持多 host,可以把多个 Docker host 上的 containers 统一管理:可以查看 images,甚至 build ...

  8. WPF 程序Form自的控件自适应方式之一

    <Window x:Class="MapEditor2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/ ...

  9. Struts2文件上传,以及各种注意事项

    首先肯定是要配置好struts2环境,这个在另一篇<struts2环境搭建,以及一个简单实例>里已经讲过了 首先是网页部分,upload_file.jsp <%@ page lang ...

  10. 使用jlink直接烧norflash或者nandflash不借助uboot的猜想

    由于喜欢折腾,我是在linux下使用jlink的,既然JLinkExe可以进行内存读写操作,loadbin等操作,并且通过指定命令文件支持批量指令输入,那么首先jlink是可以直接访问内部存储器的,包 ...