I'm designing a plugin framework for ASP.NET MVC3 using Razor views, and I'm having an issue getting the embedded views to work correctly.
The plugin framework is designed to have these features:
Now everything works fine, except when the embedded views have references to types within the plugin dll. Then I get the infamous error (names left out):
The type or namespace name '[Plugins]' does not exist in the namespace '[MyPluginSolution]' (are you missing an assembly reference?)
The reason for this is that the csc compiler which is invoked runtime to compile the razor views only get the dll references from the bin folder and the GAC.
I've also tried pre-compiling the views using this technique but in the end it gives the same results, since the runtime insists on compiling a wrapper for the pre-compiled razor view.
I could of course drop the plugin dll in the /bin folder, but my question is:
Is there a way to register dlls in a non-bin (and non-GAC) folder, and treat them as "first class citizens" so they can be used by the razor views?
Ok, the solution was found using this article.
First I create a class with a PreApplicationStartMethod
. This method scans the plugin folder and copies the dlls to the AppDomain.DynamicDirectory
.
Then each of these dll's are loaded using BuildManager.AddReferencedAssembly
.
And voilà, the strongly-typed Razor views compile beautifully. See the code here:
[assembly: PreApplicationStartMethod(typeof(MySolution.PluginHandler.PluginActivator), "Initialize")]
namespace MySolution.PluginHandler
{
public class PluginActivator
{
private static readonly DirectoryInfo PluginFolderInfo;
static PluginActivator() {
PluginFolderInfo = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));
}
public static void Initialize() {
CopyPluginDlls(PluginFolderInfo, AppDomain.CurrentDomain.DynamicDirectory);
LoadPluginAssemblies(AppDomain.CurrentDomain.DynamicDirectory);
}
private static void CopyPluginDlls(DirectoryInfo sourceFolder, string destinationFolder)
{
foreach (var plug in sourceFolder.GetFiles("*.dll", SearchOption.AllDirectories)) {
if (!File.Exists(Path.Combine(destinationFolder, plug.Name))) {
File.Copy(plug.FullName, Path.Combine(destinationFolder, plug.Name), false);
}
}
}
private static void LoadPluginAssemblies(string dynamicDirectory)
{
foreach (var plug in Directory.GetFiles(dynamicDirectory, "*.dll", SearchOption.AllDirectories)) {
Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(plug));
BuildManager.AddReferencedAssembly(assembly);
}
}
}
}
I hope this can help other programmers that want to create a clean plugin framework using these new technologies.
David Ebbo recently blogged about precompiling Razor views into assemblies. You can view the post here.
You should be able to avoid registering the assemblies directly by dynamically loading the assemblies (I would typically use my IoC container for this) and then calling BuildManager.AddReferencedAssembly for each plugin assembly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With