关于情景的一些解释但请耐心等待!!!
我已经实现了一个可插入的MVC应用程序,它可以注册主项目的Areas文件夹中存在的插件。
每个插件都有一些视图和控制器 我想为插件中的视图设置布局(插件对主应用程序中的主布局一无所知)
所以我调查了一些渲染视图的方法,我想要渲染...
在主应用程序中,我PluginBootstrapper
会将区域文件夹中的所有插件注册为:
public class PluginBootstrapper
{
public static readonly List<Assembly> PluginAssemblies = new List<Assembly>();
public static void Init()
{
var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");
foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll", SearchOption.AllDirectories))
PluginAssemblies.Add(Assembly.LoadFile(file));
PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);
// Add assembly handler for strongly-typed view models
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
}
private static Assembly AssemblyResolve(object sender, ResolveEventArgs resolveArgs)
{
var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
// Check we don't already have the assembly loaded
foreach (var assembly in currentAssemblies)
{
if (assembly.FullName == resolveArgs.Name || assembly.GetName().Name == resolveArgs.Name)
{
return assembly;
}
}
return null;
}
}
致电Init()
并在assembly
档案中注册插件:
[assembly: PreApplicationStartMethod(
typeof(PluginBootstrapper), "Init")]
另一方面,每个插件都可以在单独的解决方案中开发,因此开发的插件有自己的AreaRegistration
实现,例如我有其中一个:
public class SettingPluginAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "SettingPlugin"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"SettingPlugin",
"SettingPlugin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
我知道如何在View中设置布局,但是当所有视图都在同一个项目中并且Views知道主要布局在哪里时,这是正确的方法,但是在插件视图中必须知道主要布局在主应用程序中的位置?
例如在插件视图中:
@{
ViewBag.Title = "Plugin View Title";
Layout = "the address of main layout in main application";
}
正如this link提到的另一种方式是_ViewStart
,但这也不是正确的方法,因为每个插件都拥有它自己的_ViewStart
。
这样做有一个很好的模式,比如实施WebViewPage
来覆盖Layout
:
public abstract class SitePage<T> : System.Web.Mvc.WebViewPage<T>
{
public override string Layout
{
get
{
return base.Layout;
}
set
{
base.Layout = value;
}
}
}
或创建一个界面来设置Layout
并强制插件视图实现该界面,并通过Init()
中PluginBootstrapper
或其他内容中的反射来更改布局?
UPDATE1:
在WebPageBase
方法中注册插件时是否可以加载所有Init()
类型,并通过反射为每个类型设置Layout
?
UPDATE2:
糟糕的方式
public class BaseController : Controller
{
private string _masterName;
public string MasterLayout
{
get
{
return _masterName;
}
set
{
_masterName = value;
}
}
}
控制器:
public class SettingController : BaseController
{
public ActionResult Index()
{
var myView = View();
myView.MasterName = MasterLayout;
return myView;
}
}
和Init()
PluginBootstrapper
中的这样的内容:
var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");
foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll", SearchOption.AllDirectories))
PluginAssemblies.Add(Assembly.LoadFile(file));
foreach (Assembly plugin in PluginAssemblies)
{
BuildManager.AddReferencedAssembly(plugin);
var controllers = plugin.DefinedTypes.Where(x => x.BaseType.Name == "BaseController");
foreach (Type t in controllers)
{
// t.InvokeMember("MasterLayout", BindingFlags.SetProperty);
PropertyInfo propertyInfo = t.GetProperty("MasterLayout");
if (propertyInfo != null)
propertyInfo.SetValue(t, /*Convert.ChangeType(*/"~/Views/Shared/_Wrapper.cshtml"/*, propertyInfo.PropertyType)*/, null);
}
}
或硬代码MasterLayout
始终返回特定的布局地址。
我认为UPDATE2不是正确的方式......
提前感谢。