I am trying to set up an MVC3 solution using areas, but I want to have my areas in different assemblies. For example, I want a parent assembly that contains shared resources like master pages, style sheets, scripts, login page, etc. But I want distinct areas of business functionality in separate assemblies.
I tried this sample that was written for the MVC2 preview: http://msdn.microsoft.com/en-us/library/ee307987%28VS.100%29.aspx. (Note, I originally found this from this Stack Overflow thread: ASP.NET MVC - separating large app). But it appears that MVC3 does not have the option to move view files into the main project. I’m not crazy about using the embedded resource / VirtualPathProvider option.
Any suggestions on how to make this work with MVC3?
Thanks, Skip
1 - Seperate you Mvc Areas into differrent Mvc Projects to be compiled into their own seperate assemblies
2 - Add this to your AssemblyInfo.cs class, to call a method when the application is loaded
[assembly: PreApplicationStartMethod(typeof(PluginAreaBootstrapper), "Init")]
3 - Here's what the Init method looks like when it's invoked during the load
public class PluginAreaBootstrapper
{
public static readonly List<Assembly> PluginAssemblies = new List<Assembly>();
public static List<string> PluginNames()
{
return PluginAssemblies.Select(
pluginAssembly => pluginAssembly.GetName().Name)
.ToList();
}
public static void Init()
{
var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");
foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll"))
PluginAssemblies.Add(Assembly.LoadFile(file));
PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);
}
}
4 - Add a custom RazorViewEngine
public class PluginRazorViewEngine : RazorViewEngine
{
public PluginRazorViewEngine()
{
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
AreaPartialViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
var areaViewAndPartialViewLocationFormats = new List<string>
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
var partialViewLocationFormats = new List<string>
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
var masterLocationFormats = new List<string>
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
foreach (var plugin in PluginAreaBootstrapper.PluginNames())
{
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{1}/{0}.cshtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{1}/{0}.vbhtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{0}.cshtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.vbhtml");
}
ViewLocationFormats = partialViewLocationFormats.ToArray();
MasterLocationFormats = masterLocationFormats.ToArray();
PartialViewLocationFormats = partialViewLocationFormats.ToArray();
AreaPartialViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
AreaViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
}
}
5 - Register your Areas from your different Mvc (Area) Projects
namespace MvcApplication8.Web.MyPlugin1
{
public class MyPlugin1AreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "MyPlugin1"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyPlugin1_default",
"MyPlugin1/{controller}/{action}/{id}",
new {action = "Index", id = UrlParameter.Optional}
);
}
}
}
Sourcecode and additional references can can be found here: http://blog.longle.io/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas/