C# 当控制器类位于不同程序集中时,如何将控制器注册到ASP.NET MVC?
我的目标是修改asp.net mvc的控制器注册表,这样我就可以在单独的(子)程序集中创建控制器和视图,只需将视图文件和DLL复制到主机mvc应用程序,新控制器就可以有效地“插入”到主机应用程序 显然,我需要某种国际奥委会的模式,但我不知所措 我的想法是引用system.web.mvc的子程序集,然后开始构建继承自C# 当控制器类位于不同程序集中时,如何将控制器注册到ASP.NET MVC?,c#,asp.net,asp.net-mvc,plugins,inversion-of-control,C#,Asp.net,Asp.net Mvc,Plugins,Inversion Of Control,我的目标是修改asp.net mvc的控制器注册表,这样我就可以在单独的(子)程序集中创建控制器和视图,只需将视图文件和DLL复制到主机mvc应用程序,新控制器就可以有效地“插入”到主机应用程序 显然,我需要某种国际奥委会的模式,但我不知所措 我的想法是引用system.web.mvc的子程序集,然后开始构建继承自controller的控制器类: 独立组件: using System.Web; using System.Web.Mvc; namespace ChildApp { pub
controller
的控制器类:
独立组件:
using System.Web;
using System.Web.Mvc;
namespace ChildApp
{
public class ChildController : Controller
{
ActionResult Index()
{
return View();
}
}
}
是的,一切都很好。但后来我考虑修改主机应用程序的控制器注册表,以便在运行时加载新的子控制器,我感到困惑。也许是因为我需要对C#有更深的理解
无论如何,我认为我需要创建一个CustomControllerFactory
类。因此,我开始编写一个类来重写GetControllerInstance()
方法。当我打字时,intellisence弹出了这样一条消息:
主机MVC应用程序:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
public class ControllerLoader
{
public static IList<IController> Load(string folder)
{
IList<IController> controllers = new List<IController>();
// Get files in folder
string[] files = Directory.GetFiles(folder, "*.plug.dll");
foreach(string file in files)
{
Assembly assembly = Assembly.LoadFile(file);
var types = assembly.GetExportedTypes();
foreach (Type type in types)
{
if (type.GetInterfaces().Contains(typeof(IController)))
{
object instance = Activator.CreateInstance(type);
controllers.Add(instance as IController);
}
}
}
return controllers;
}
}
现在,在这一点上我不知所措。我不知道那有什么用。最初,我打算编写一个“控制器加载器”类,如服务定位器,如下所示:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { typeof(HomeController).Namespace }
);
}
主机MVC应用程序:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
public class ControllerLoader
{
public static IList<IController> Load(string folder)
{
IList<IController> controllers = new List<IController>();
// Get files in folder
string[] files = Directory.GetFiles(folder, "*.plug.dll");
foreach(string file in files)
{
Assembly assembly = Assembly.LoadFile(file);
var types = assembly.GetExportedTypes();
foreach (Type type in types)
{
if (type.GetInterfaces().Contains(typeof(IController)))
{
object instance = Activator.CreateInstance(type);
controllers.Add(instance as IController);
}
}
}
return controllers;
}
}
公共类控制器加载程序
{
公共静态IList加载(字符串文件夹)
{
IList控制器=新列表();
//获取文件夹中的文件
string[]files=Directory.GetFiles(文件夹“*.plug.dll”);
foreach(文件中的字符串文件)
{
Assembly=Assembly.LoadFile(文件);
var types=assembly.GetExportedTypes();
foreach(类型中的类型)
{
if(type.GetInterfaces()包含(typeof(IController)))
{
对象实例=Activator.CreateInstance(类型);
添加(作为IController的实例);
}
}
}
返回控制器;
}
}
然后我计划用我的控制器列表在控制器工厂注册它们。但是怎样?我觉得我快要弄明白了。我想这一切都归结为一个问题:如何连接到
returnbase.GetControllerInstance(requestContext,controllerType)代码>?或者,我应该使用一种完全不同的方法吗?我对控制器的经验是,这种方法很有效。将程序集复制到bin目录中(或添加对程序集的引用,它将为您复制到该目录中)
我没有尝试使用视图引用“根”ASP.NET MVC项目中的其他程序集。如果控制器位于另一个命名空间中,则需要在global.asax中修改路由,如下所示:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { typeof(HomeController).Namespace }
);
}
请注意MapRoute
方法的附加namespaces
参数。您的其他项目应该像区域一样设置,您将获得所需的结果。区域注册课程将把你的区域项目纳入其中。然后,您可以将dll放入正在运行的应用程序中,它将在不构建整个应用程序并重新部署它的情况下运行
最简单的方法是向解决方案中添加一个新的mvc项目,并让Visual Studio在/areas/mynewprog/中创建新项目。删除所有不需要的文件,并添加一个区域注册类来连接它
构建项目,抓取它的dll并将其放入正在运行的网站bin目录中,它的
然后,您只需处理视图和内容,或者将它们编译到dll中,或者将它们复制到服务器的areas/mynewproj/文件夹中。创建一个单独的模块,如插件项目结构所示:
请注意,某些文件已从项目中排除
MessagingController.cs文件如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MessagingModule.Controllers
{
public class MessagingController : Controller
{
//
// GET: /Messaging/
public ActionResult Index()
{
var appName = Session["appName"]; // Get some value from another module
return Content("Yep Messaging module @"+ appName);
}
}
}
MessaginArearRegistration文件如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace MessagingModule
{
public class MessagingAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Messaging";
}
}
public override void RegisterArea( AreaRegistrationContext context )
{
context.MapRoute(
"Messaging_default",
"Messaging/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
//new[] { "MessagingModule.Controllers" }
);
}
}
}
Global.asax.cs文件的相关药剂如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MessagingModule.Controllers
{
public class MessagingController : Controller
{
//
// GET: /Messaging/
public ActionResult Index()
{
var appName = Session["appName"]; // Get some value from another module
return Content("Yep Messaging module @"+ appName);
}
}
}
[将外部插件(控制器、api控制器库)加载到当前应用程序域中。在此之后,asp.net mvc将为您完成其余工作(区域注册和控制器发现)[3]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Reflection;
using System.Web.Configuration;
using System.Text.RegularExpressions;
namespace mvcapp
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var domain = AppDomain.CurrentDomain;
domain.Load("MessagingModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
即使这两个项目没有相互引用,那也行吗?我正在尝试这样做,这样我就可以在不重新编译宿主应用程序的情况下将控制器添加到实时网站。对不起,没有运气。我将ChildApp DLL复制到了HostApp bin,但没有运气。你确定吗?我有相同的目标,可以发誓只需要重新启动(不过还是太多了)你的名称空间是一样的吗?我想这样做是可行的,但是如果ChildController在ChildApp名称空间中,而宿主项目在HostApp名称空间中,那么它可能不起作用。你能确认吗?这就是问题所在。我正试图在不为每个新的控制器程序集重新编译根/主机应用程序的情况下让它起作用。等等……waiiiiit。也许这在没有引用的情况下可以工作!我正在尝试namespaces:new[]{typeof(IController).Namespace}
。我们将在不添加从HostApp到ChildApp的引用的情况下看看这是否有效。@quakkels-ARGH你正在用悬念扼杀我们……我厌倦了WaiiIIT,它有效吗?!@Nenotlep-你是什么意思?我将它标记为答案。这是几年前的问题。这里有一个更新的版本:你有更详细的答案吗关于此的示例或教程?我真的很想进一步研究此方法,因为它看起来非常优雅和动态。编译的视图与Razor一起工作吗?这样Razor布局在主机应用程序中,视图本身在DLL中?除了加载程序集时会引发FileNotFound
异常。