本章主要讲解如何为框架新增插件化开发功能。
在.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升级之八:实现简易插件化开发的更多相关文章
-
Android插件化开发
客户端开发给人的印象往往是小巧,快速奔跑.但随着产品的发展,目前产生了大量的门户型客户端.功能模块持续集成,开发人员迅速增长.不同的开发小组开发不同的功能模块,甚至还有其他客户端集成进入.能做到功能模 ...
-
【我的Android进阶之旅】Android插件化开发学习资料
1.目前开源的插件开发框架大致有哪些? 1. 任玉刚 的 dynamic-load-apk Github 地址:https://github.com/singwhatiwanna/dynamic-lo ...
-
Android 插件化开发(四):插件化实现方案
在经过上面铺垫后,我们可以尝试整体实现一下插件化了.这里我们先介绍一下最简单的实现插件化的方案. 一.最简单的插件化实现方案 最简单的插件化实现方案,对四大组件都是适用的,技术面涉及如下: 1). 合 ...
-
Android组件化和插件化开发
http://www.cnblogs.com/android-blogs/p/5703355.html 什么是组件化和插件化? 组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Modul ...
-
Android 使用动态载入框架DL进行插件化开发
如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 (来自时之沙的csdn博客) 概述: 随着应用的不断迭代.应用的体积不断增大,项目越来越臃肿,冗余添 ...
-
Android插件化开发---执行未安装apk中的Service
欢迎各位增加我的Android开发群[257053751] 假设你还不知道什么叫插件化开发.那么你应该先读一读之前写的这篇博客:Android插件化开发,初入殿堂 上一篇博客主要从总体角度分析了一下 ...
-
NET 平台下的插件化开发内核
.NET 平台下的插件化开发内核(Rabbit Kernel) 每个程序猿都有一个框架梦,曾经在2013年8月15日写过一篇“Koala Framework是什么?我为什么要写这个框架?”的文章, ...
-
JavaScript插件化开发
大熊君JavaScript插件化开发 一,开篇分析 Hi,大家好!大熊君又和大家见面了,还记得昨天的那篇文章吗------这个系列的开篇(第一季).主要讲述了以“jQuery的方式如何开发插件”, 那 ...
-
插件化开发—动态加载技术加载已安装和未安装的apk
首先引入一个概念,动态加载技术是什么?为什么要引入动态加载?它有什么好处呢?首先要明白这几个问题,我们先从 应用程序入手,大家都知道在Android App中,一个应用程序dex文件的方法数最大不能超 ...
随机推荐
-
C#中日期和时间相加的方法
可能对于初入此行业人来说有些困惑,实现起来有一丝复杂. 比如说时间是:2016-08-05 14:46:30,中间过了56秒钟.要求得出56秒之后的时间格式是:年月日时分秒 下面介绍最简单的办法, m ...
-
1122MySQL性能优化之 Nested Loop Join和Block Nested-Loop Join(BNL)
转自http://blog.itpub.net/22664653/viewspace-1692317/ 一 介绍 相信许多开发/DBA在使用MySQL的过程中,对于MySQL处理多表关联的方式或者说 ...
-
7.2.3 使用RenderTargetBitmap类生成图片
RenderTargetBitmap类可以将可视化对象转换为位图,也就是说它可以将任意的UIElement以位图的形式呈现.那么我们在实际的编程中通常会利用RenderTargetBitmap类来对U ...
-
xcode升级,报错 libxml/tree.h not found (Xcode4.6解决方案)
转:http://blog.csdn.net/yangxuanlun/article/details/8639075 Xcode升级到4.6以后,他妈的,libxml/tree.h找不到了,搞了大半天 ...
-
jdbc - Insert &#39;Date&#39; value in PreparedStatement
“preparedStatement.setDate()”方法接受的是 'java.sql.Date' 类型的参数,而我们一般格式化日期所使用的是'java.util.Date'中的'SimpleDa ...
-
Subsets —— LeetCode
Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset must ...
-
sql的基本查询语句
--------------------------------------------基本常用查询-------------------------------------- 自己简单练习做了个表. ...
-
frag
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=&qu ...
-
Windows Server 2012 R2 双网卡绑定
双网卡绑定主要有以下两点好处: 1.实现网络容错:主主模式和主被模式 2.带宽聚合 首先准备工作需要两台虚拟机,Server01是目标服务器,需要有两块网卡,并且清空两块网卡的现有配置,Server0 ...
-
Linux 网络编程 入门-常用函数
网络连接无外乎服务器和客户端两方面的编程. 对于服务器大致的流程是:1---调用socket函数创建套接字 2---调用bind函数分配IP地址和端口号 3---调用listsen函数将套接字转为可接 ...