Some explanation about scenario but please be patient to end!!!
I have Implemented a pluggable MVC application which can register plugins which exist in Areas folder of main Project.
each plugin have some views and controller I want to set layout for views in plugins(the plugins don't know anything about master layout in Main application)
So I investigated some ways to render views where I want to be rendered...
In Main Application my PluginBootstrapper
will register all plugins in Areas folder is as:
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;
}
}
To call Init()
and register plugin in assembly
file:
[assembly: PreApplicationStartMethod(
typeof(PluginBootstrapper), "Init")]
In the other side each plugin can be developed in separate solution so developed plugin have it's own AreaRegistration
implementation for example for one of them I have:
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 }
);
}
}
I know how we can set layout in View but this is proper way for when all the views are in same project and Views knows where the main layout is, but in plugin views must know where the main Layout located in main application?
For example in plugin view:
@{
ViewBag.Title = "Plugin View Title";
Layout = "the address of main layout in main application";
}
As this link mentioned the other way is _ViewStart
but this is also not proper way because each plugin have it's own _ViewStart
.
So is there a good pattern to do this such as implementing WebViewPage
to override Layout
:
public abstract class SitePage<T> : System.Web.Mvc.WebViewPage<T>
{
public override string Layout
{
get
{
return base.Layout;
}
set
{
base.Layout = value;
}
}
}
or make a interface to set Layout
and force the plugins view to implement that interface and change the layout by the reflections in Init()
in PluginBootstrapper
or something else ?
UPDATE1:
Is it possible or a good way to load all WebPageBase
types while registering plugins in Init()
method and set Layout
for each of them by reflection ?
UPDATE2:
The bad way
public class BaseController : Controller
{
private string _masterName;
public string MasterLayout
{
get
{
return _masterName;
}
set
{
_masterName = value;
}
}
}
for controller:
public class SettingController : BaseController
{
public ActionResult Index()
{
var myView = View();
myView.MasterName = MasterLayout;
return myView;
}
}
and in Init()
in PluginBootstrapper
something like this:
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);
}
}
or hard code MasterLayout
to always return specific layout address.
I believe UPDATE2 is not proper way...
thanks in advance.