MVC | 插件式编程

1>


首先创建主站,这个主站就是一个MVC项目,名字叫MVC.MainSite。然后再项目下添加一个Plugins目录文件夹。

然后创建一个插件,这个插件也是一个MVC项目名字叫MVC.Pluings.Man,在插件MVC中创建一个Home控制器,然后生成一下。这时候我们在主站MVC.MainSite的Plugins文件夹下再添加一个Man文件夹,用于存放插件MVC.Pluings.Man的文件(例如dll文件,和视图文件),----->然后我们再在Man文件夹下创建一个bin文件夹,专门用于存在MVC.Pluings.Man的dll文件。------->然后从插件MVC.Pluings.Man项目的bin目录下复制MVC.Pluings.Man.dll文件文件到主站MVC.MainSite下的Plugins/Man/bin文件夹中去(如果有视图的话,还需要将整个Views文件夹都复制到Man文件夹下)

MVC.Pluings.Man下的Home控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVC.Pluings.Man.Controllers
{
    public class HomeController : Controller
    {

        public ActionResult Index() //这个action返回的是文本
        {
            return Content("男士区");
        }

        public ActionResult Add() //这个action返回的是视图
        {
            return View();
        }
    }
}

现在我们添加一个名字叫MVC.PluginsFramework的类库,在类库中添加一个PlugsControllerFactory.cs类 ,给这个类库添加一个System.Web.Mvc的引用(需要注意版本与主站的System.Web.Mvc版本一致),和添加一个using System.Web;的引用 

(我们为什么要创建一个类库,主要作用就是要在主站中创建插件控制类的对象实例。)

MVC.PluginsFramework类库下的PlugsControllerFactory.cs类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PluginsFramework
{
    using System.IO;
    using System.Reflection;
    using System.Web;
    using System.Web.Mvc;
    public class PlugsControllerFactory : DefaultControllerFactory
    {
        //DefaultControllerFactory类是管道事件中第7个事件中的一个过滤器(MVC独有的) 我们在这里自定义一个类,并重写他的GetControllerType方法
        //DefaultControllerFactory过滤器中做的第三件事情就是通过反射创建控制器类对象实例。

        //参数requestContext 是你请求的上下文,参数controllerName是你请求的控制器 
        protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
        {

            //其实MVC插件式开发其实就一个网站的程序集放到一个主站网站中。假设插件网站的有一个Home控制器,而主站网站也有一个Home控制器,如果将插件网站的dll放入到主网站的bin下,这样就会造成主网站bin目录下的程序集里有2个Home控制器类。到时候就不知道创建哪个Home的对象实例了。所以我们会在主网站中单独创建一个存放插件的文件夹叫Plugins文件夹。
            //因为 base.GetControllerType(requestContext, controllerName)默认查找的是网站的的bin目录下的所有程序集。那现在我们要去Plugins文件夹中查找程序集,所有我们就要遍历这个文件夹下的所有程序集


            //1.0自定义控制器类的查找路径先去插件目录下找,如果找到了则直接创建其Type返回,否则继续默认的去创建
            controllerName = controllerName + "Controller"; //(为什么参数中controllerName是没有带Controller后缀的)

            //2.0扫描插件目录下的程序集
            //2.0.1获取Plugins文件夹的物理路径。 //获取应用程序域下的当前应用程序域的基目录。
            string basePath = AppDomain.CurrentDomain.BaseDirectory; //于是获取到了 D:\ASPNET\MvcAppPluings\MVC.MainSite\
            string plugsPath = basePath + @"Plugins/";

            //2.0.2扫描Plugins目录下所有的程序集
            string[] filesPath = Directory.GetFiles(plugsPath, "*.dll", SearchOption.AllDirectories);

            Type type = null;

            if (filesPath.Any())
            {
                foreach (string filePath in filesPath)
                {
                    Assembly asm = Assembly.LoadFile(filePath);
                    type = asm.GetType("MVC.Pluings.Man.Controllers." + controllerName, false, true); //注意这需要加controllerName这个插件控制器类的命名空间。到时候可以给它写入到web.config中去

                    if (type != null)
                    {
                        break; //既然找到这个控制器类了,就不再循环遍历了。退出循环
                    }
                }
            }

            if (type != null)
            {
                return type; //返回找到的控制器类
            }

            //默认查找的是网站的的bin目录下的所有程序集。
            return base.GetControllerType(requestContext, controllerName);


            //特别要注意:当我们继承并重写了DefaultControllerFactory控制器工厂类的时候,我们应该告诉MVC框架,控制器工厂换成我们自己定义的PlugsControllerFactory
            //这时候应该去Global.asax文件中,在最后面添加一句

        }
    }
}

Global.asax

namespace MVC.MainSite
{
    // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
    // 请访问 http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //告诉MVC框架,控制器工厂换成我们自己定义的PlugsControllerFactory
            ControllerBuilder.Current.SetControllerFactory(new PlugsControllerFactory());

            //将视图引擎替换成我们自己定义的视图引擎
            ViewEngines.Engines.Clear(); //移除所有的视图引擎
            ViewEngines.Engines.Add(new PlugsRazorViewEngie()); //将自己定义的视图引擎追加到视图引擎集合当中去

        }
    }
}

如果插件中项目MVC.Pluings.Man中的Home控制类的action返回的是return Content("....") 那么做到以上就可以了,但是如果action返回的是 return View() 那么就需要将插件项目MVC.Pluings.Man下的View文件夹全部复制到主站的项目MVC.MainSite下的Plugins文件夹下的Man文件夹下,到时候主站项目运行的时候需要找视图,然后将视图编译成一个类(程序集)的,那时候我们就让它去主站下的Plugins文件夹下的Man文件夹下找对应的视图。


如果不将Views文件夹移动到主站下的话,在主站中运行插件中的Home控制器下的Add方法就会报错了


(需要将这个View文件夹整体都移动到主站MVC.MainSite下的的Plugins文件夹下的Man文件夹下)



这里需要对FindView重写。

给类库MVC.PluginsFramework添加一个类PlugsRazorViewEngie.cs (再给MVC.PluginsFramework类库添加一个System.Web.WebPages.Razor.dll的引用,这个dll去主站这个MVC应用程序的bin目录中找,然后引用到类库中)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PluginsFramework
{
    using System.Reflection;
    using System.Web.Mvc;
    using System.Web.WebPages.Razor;
    public class PlugsRazorViewEngie : RazorViewEngine
    {
        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            //return base.FindView(controllerContext, viewName, masterName, useCache);它默认是去主站的View文件夹下找视图。那现在我们将插件网站的控制器的视图放到Plugins文件夹下,所以我们要告诉MVC框架先去Plugins文件夹下找视图。如果找不到才到主站的的更目录的View文件夹下找视图

            //设置视图位置格式。
            string[] viewLocationFormats ={
                        "~/Plugins/Man/Views/{1}/{0}.cshtml", //{1}表示控制器,{0}表示action
                        "~/Plugins/Man/Views/Shared/{0}.cshtml",
                        "~/Views/{1}/{0}.cshtml",
                        "~/Views/Shared/{0}.cshtml"
                                          };
          

            //替换父类中的ViewLocationFormats,换成我们自己定义的目录(即:视图引擎默认会去网站根目录下的Views目录下找用户请求的视图,这里就自己指定视图引擎去我指定的路径下找视图文件)
            base.ViewLocationFormats = viewLocationFormats;
            

            //2.0将你的Razor视图编译成前台代码的时候有个开始事件,这个事件就是CodeGenerationStarted
            //2.0当视图编译成类(程序集)以后,则执行RazorBuildProvider_CodeGenerationStarted方法(这个方法的作用其实就是将视图编译成程序集后通过代码的形式给它添加一个引用)
            RazorBuildProvider.CodeGenerationStarted += RazorBuildProvider_CodeGenerationStarted;


            //它默认是去主站的View文件夹下找视图。
            return base.FindView(controllerContext, viewName, masterName, useCache);
        }

        
        /// <summary>
        /// 负责将当前视图所对应的控制器类所在的程序集编译成视图所在的程序集的依赖引用(例如,我们到时候会将Add.cshtml这个文件编译成一个程序集,而这个程序集需要依赖另外一个程序集,这个程序集就是这个Add.cshtml视图的所对应的Home控制器所在的程序集MVC.Pluings.Man.dll。这里就是将这个MVC.Pluings.Man.dll程序集以代码的形式引用到"Add.cshtml这个文件编译成程序集后"的程序集中去)
        /// </summary>
        /// <param name="sender">谁点出了这个CodeGenerationStarted方法那么这个send就是谁,所以这这个send是RazorBuildProvider类的对象</param>
        /// <param name="e"></param>
        void RazorBuildProvider_CodeGenerationStarted(object sender, EventArgs e)
        {
            RazorBuildProvider obj = sender as RazorBuildProvider;

            //1.0首先我们就应该获取网站更目录的路径
            string basePath = AppDomain.CurrentDomain.BaseDirectory;

            //1.0.1获取Plugins的目录
            string plugsPath = basePath + @"Plugins/";

            //1.0.2获取plugins目录下,名字为MVC.Pluings.Man.dll这个程序集的路径(虽然方法的返回值是一个数组,但是它里面肯定只存在一个元素)
            string[] filesPath = System.IO.Directory.GetFiles(plugsPath, "MVC.Pluings.Man.dll", System.IO.SearchOption.AllDirectories);

            //如果存在MVC.Pluings.Man.dll这个文件
            if (filesPath[0].Any())
            {
                //将MVC.Pluings.Man.dll加载到内存中
                Assembly asm = Assembly.LoadFrom(filesPath[0]);

                //将asm这个程序集增加到 RazorBuildProvider 类中;
                //AssemblyBuilder是用来定义和创建动态的程序集的
                //AddAssemblyReference(asm)表示向你现在正在编译的程序集里面添加一个依赖项目(即:添加一个依赖程序集。其实就是添加一个引用)
                obj.AssemblyBuilder.AddAssemblyReference(asm);
            }
        }
    }
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值