对ASP.NET中的MVC引擎开发插系统进行详解


本文摘自PHP中文网,作者巴扎黑,侵删。

这篇文章主要为大家详细介绍了使用ASP.NET MVC引擎开发插件系统的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

一、前言

我心中的插件系统应该是像Nop那样(更牛逼的如Orchard,OSGI.NET),每个插件模块不只是一堆实现了某个业务接口的dll,然后采用反射或IOC技术来调用,而是一个完整的mvc小应用,我可以在后台控制插件的安装和禁用,目录结构如下:

生成后放在站点根目录下的Plugins文件夹中,每个插件有一个子文件夹

Plugins/Sms.AliYun/

Plugins/Sms.ManDao/

我是一个有强迫症的的懒人,我不想将生成的dll文件拷贝到bin目录。

二、要解决的问题

1.asp.net引擎默认只会加载“bin”文件夹中的dll,而我们想要的插件文件则是分散在Plugins目录下的各个子目录中。

2.视图中使用了模型时如何处理?默认情况下RazorViewEngine使用BuildManager将视图编译成动态程序集,然后使用Activator.CreateInstance实例化新编译的对象,而使用插件dll时,当前的AppDomain不知道如何解析这种引用了模型的视图,因为它不存在于“bin”或GAC中。更糟糕的是,不会收到任何错误消息,告诉您为什么它不工作,或者问题在哪。相反,他会告诉你,从View目录中找不到文件。

3.某个插件正挂在站点下运行着,直接覆盖插件的dll,会告诉你当前dll正在使用,不能被覆盖。

4.视图文件不放站点的View目录中,该如何加载。

三.Net 4.0让这一切变成可能

Net4.0有一个新特性是在应用程序初始化之前执行代码的能力(PreApplicationStartMethodAttribute),这个特性使得应用程序在Application_Star前可以做一些工作,例如我们可以在应用启动之前告知我们的mvc插件系统的dll放在哪,做预加载处理等。关于.net的几个新特性,有歪果仁写得有博客来介绍,点我。,关于PreApplicationStartMethodAttribute,有博友已经写过了,点我。 Abp的启动模块应该也是使用PreApplicationStartMethodAttribute这个特性原理来实现的,具体是不是这样还没看。

四、解决方案

1.修改主站点web.config目录,让运行时除了加载bin目录中的文件,还可以从其它目录加载


1

2

3

4

5

<runtime>

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

 <probing privatePath="Plugins/temp/" />

</assemblyBinding>

</runtime>

2.开发一个简易的插件管理类,这个类的作用就是在Application_Start之前就把Plugins各子目录中的dll拷贝到第1步指定的文件夹中,为了让demo尽可能简单,没有对重复的dll进行检测(比如插件中引用了ef程序集,主站点也引用了,在站点bin目录中已经存在ef的dll了,就没必要再把插件中的dll拷贝到上面设置的动态程序集目录中)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Reflection;

using System.Text;

using System.Threading.Tasks;

using System.Web;

using System.Web.Compilation;

using System.Web.Hosting;

[assembly: PreApplicationStartMethod(typeof(Plugins.Core.PreApplicationInit), "Initialize")]

namespace Plugins.Core

{

 public class PreApplicationInit

 {

 

  static PreApplicationInit()

  {

   PluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));

   ShadowCopyFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins/temp"));

  }

 

  /// <summary>

  /// 插件所在目录信息

  /// </summary>

  private static readonly DirectoryInfo PluginFolder;

 

  /// <summary>

  /// 程序应行时指定的dll目录

  /// </summary>

  private static readonly DirectoryInfo ShadowCopyFolder;

 

  public static void Initialize()

  {

   Directory.CreateDirectory(ShadowCopyFolder.FullName);

   //清空插件dll运行目录中的文件

   foreach (var f in ShadowCopyFolder.GetFiles("*.dll", SearchOption.AllDirectories))

   {

    f.Delete();

   }

   foreach (var plug in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories).Where(i=>i.Directory.Parent.Name== "plugins"))

   {

    File.Copy(plug.FullName, Path.Combine(ShadowCopyFolder.FullName, plug.Name), true);

   }

   foreach (var a in

    ShadowCopyFolder

    .GetFiles("*.dll", SearchOption.AllDirectories)

    .Select(x => AssemblyName.GetAssemblyName(x.FullName))

    .Select(x => Assembly.Load(x.FullName)))

   {

    BuildManager.AddReferencedAssembly(a);

   }

 

  }

 }

}

3.如何让View引擎找到我们的视图呢?答案是重写RazorViewEngine的方法,我采用了约定大于配置的方式(假设我们的插件项目命名空间为Plugins.Apps.Sms,那么默认的控制器命名空间为Plugins.Apps.Sms.Controllers,插件生成后的文件夹必须为/Plugins/Plugins.Apps.Sms/),通过分析当前控制器就可以知道当前插件的View目录位置


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using System.Web;

using System.Web.Mvc;

using System.Web.WebPages.Razor;

 

namespace Plugins.Web

{

 public class CustomerViewEngine : RazorViewEngine

 {

 

  /// <summary>

  /// 定义视图页所在地址。

  /// </summary>

  private string[] _viewLocationFormats = new[]

  {

   "~/Views/Parts/{0}.cshtml",

   "~/Plugins/{pluginFolder}/Views/{1}/{0}.cshtml",

   "~/Plugins/{pluginFolder}/Views/Shared/{0}.cshtml",

   "~/Views/{1}/{0}.cshtml",

   "~/Views/Shared/{0}.cshtml",

  };

  public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)

  {

   string ns = controllerContext.Controller.GetType().Namespace;

   string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");

   //说明是插件中的控制器,View目录需要单独处理

   if (ns.ToLower().Contains("plugins"))

   {

    var pluginsFolder = ns.ToLower().Replace(".controllers", "");

    ViewLocationFormats = ReplacePlaceholder(pluginsFolder);

   }

   return base.FindView(controllerContext, viewName, masterName, useCache);

  }

  /// <summary>

  /// 替换pluginFolder占位符

  /// </summary>

  /// <param name="folderName"></param>

  private string[] ReplacePlaceholder(string folderName)

  {

   string[] tempArray = new string[_viewLocationFormats.Length];

   if (_viewLocationFormats != null)

   {

    for (int i = 0; i < _viewLocationFormats.Length; i++)

    {

     tempArray[i] = _viewLocationFormats[i].Replace("{pluginFolder}", folderName);

    }

   }

   return tempArray;

  }

 }

}

然后在主站点的Global.asax中将Razor引擎指定为我们重写过的

4.开始制作一个插件目录,跟我们平时建立的MVC项目并没有太大区别,只是发布时需要做一些设置。

.生成路径要按照第3条的约定来写,不然会找不到视图文件

.View目录下的web.config和.cshtml文件要复制到生成目录(在文件中点右键)

3.设置引用项目中的生成属性,主程序下面已经有了的就把“复制到输出目录”设置为无,要不然拷贝到动态bin目录时会出错,可以对第2步中的那个类改造一下,加入文件比较功能,bin目录中没有的,才拷贝到动态bin目录中。

4.生成后的目录结构如下:

5.跑一下,一切正常,插件中的控制器工作正常,视图中引用了Model也没问题

到此,一个插件系统的核心部分就算完成了,你可继续进行扩展,增加插件的发现、安装、卸载功能,这些相对于核心功能来说,都是小儿科。后续我会基于Abp框架出一个插件系统的文章,有兴趣的把小板凳准备好,瓜子花生买上:)

以上就是对ASP.NET中的MVC引擎开发插系统进行详解的详细内容!

相关阅读 >>

.net mvc从视图传参到控制器的3种形式

asp.net mvc如何使用bootstrap的实例分析

.net通过母版实现页脚效果代码实例

wpf开发surface dial实例教程

asp.net mvc 遇到json循环调用的问题应该怎么解决?

asp.net core mvc中如何把二级域名绑定到特定的控制器上

解析webgrid - 非常有用的 asp.net web 帮助器

介绍oom中automapper的使用方法

两个需要在asp.net程序中值得注意的地方

分享asp.net学习笔记(11)webpages php

更多相关阅读请进入《asp.net》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...