ASP.NET Dropin DLL Plugin – Part Two

ASP.NET Dropin DLL Plugin – Part One

The first post was a quick intro to the project. In this post, we will cover this works in more detail. So let’s just jump right in.

Typically in ASP.NET MVC, the framework knows where to look for files based on the conventions of the framework. Views are in view folder. With a plugin, the framework needs to be told if a files exists in a plugin DLL and be given a stream to the file. This where the System.Web.Hosting.VirtualPathProvider and System.Web.Hosting.VirtualFile come into play.

In the example there are two class that inherit from the above two classes: AssembleVirtualPathProvider and AssembleVirtualFile. These are Terrible implementations because they are only looking for the one plugin dll. This is a major area for improvement as these classes should look within any DLL that is a plugin. There are many options for this but that is another topic.

The AssembleVirtualPathProvider main job is to see if a view exists within a dll. To do this a number of methods need to be overridden. These methods by default look for views following MVC default naming and file location conventions. In order to check inside of the assembles, these methods need to be overridden and code added to look for the views within the DLLs. Make sure to call the base methods! If you don’t MVC will not be able to find the files local to main MVC project.

The AssembleVirtualFile is a representation of the a file being loaded from a plugin. It has one job, to open a file stream to the file that is in a dll and return it. That is it. This class is used by the AssembleVirtualPathProvider to return a file when GetFile method is called.

Once these are created, the main MVC project must be told to use these providers. In the main MVC project’s global.asax, the new path provider needs to be registered.

System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new Lib.AssembleVirtualPathProvider());

Now there is a catch to this. Plugins must be in the main MVCs project’s bin directory. I have tried multiple ways to get around this but have not had any luck. The reason is when DLLs are in the bin directory, the website’s process does a deep inspection of all the files in the bin directory. It keeps tabs on what files exist, exception model and controllers. If the DLLs are not in the bin directory, MVC will not be able to resolve the models and controller classes.

Moving on, serving static files from the DLLs. One of the goals of this was that the code in the plugins was written as close as possible to a normal MVC application. I did not want to have some special syntax for plugins vs non plugins. In order for this works, images and files need to be handled by a HTTP handler. In this case a static handler. Bring in the System.Web.StaticFileHandler. This will serve files from DLLs or the file system. It is pretty handy. In the web.config of the main MVC project an entry needs to be added for each static file type you would like to serve from the plugins.

<add name="AspNetStaticFileHandler-GIF" path="*.gif" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="AspNetStaticFileHandler-JPG" path="*.jpg" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="AspNetStaticFileHandler-PNG" path="*.png" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="AspNetStaticFileHandler-JS" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />

On top of this, routes need to be ignored for static file extensions that are going to be handled by the StaticFileHandler.

routes.IgnoreRoute("{*staticfile}", new { staticfile = @".*\.(css|js|gif|jpg)(/.*)?" });

But wait, another catch! System.Web.StaticFileHandler does not correctly set the HTTP response headers correctly for caching when serving files from plugins. It works perfectly when serving files from the file system. In order to fix this, a http module needs to be created that looks to see if the file was served from the StaticFileHandler and set the cache headers or use a different StaticFileHandler. Super secret 3rd option (which is sort not good), is to serve all static files from the main MVC project.

Generally speaking that is it. No hidden projects, mirrors or DLL references. A bonus is the plugins will run independently from the main MVC project when doing development if needed.

Some areas that can be improved.
-Better assembly handling in the file and path providers.
-Loaded routes, filters, ect from plugins using MEF (or similar)
-Use/write a better static file handler

Github repo with example: https://github.com/Oobert/ASP.NET-MVC-Plugins

Tags: , ,

6 Responses to “ASP.NET Dropin DLL Plugin – Part Two”

  1. Mohamed Ramadan Says:

    Thanks for your useful example, this is really clean and very good.

    I have a question if you tried to test the performance of it, are there any noticeable performance changes between this and using a native MVC website?

    Also about PluginOne project, there are a lot of unused files, I think you can exclude all the following from the it:
    - APP_Start
    - Content/Site.css
    - Global.ascx
    - Views/ all files/folders except PluginOne folder

    I really like how is your solution is simple and clean, I think you just need to explain it in more details in the blog post. for example you have to mention that all views, js, and css files in the plugin project shoule be Embedded Resource instead of Content in the Build Action.

  2. Mohamed Ramadan Says:

    Also you shouldn’t need to add a reference for “ASPNET.Plugin.PluginOne” in “ASPNET.Plugin.Master”. I think you can just change “ASPNET.Plugin.PluginOne” to produce the build folder to “..\ASPNET.Plugin.Master\bin”

  3. Tony Says:

    Thanks for the feedback.

    I have not run performance test. I have some updates to make to this solution. Specifically to the virtual path provider. Once that is done, I will try to run some tests to see. My assumption is that it will be close to the same, if not the same depending on how the virtual path provider is coded.

    The extra files could be deleted. However one of the things I wanted was for the plugin to run on its own during development. Which it can do. It would be possible for some of these files to be links to the same files in master.

    You are correct the reference does not need to exist. The only reason is there is just for simplicity. By having the reference, the DLL gets copied to the Master’s bin directory.

  4. Graeme Miller Says:

    Did you ever get talifun web to work with Virutal path provider? If so could you share your web.config. I am struggling with caching large js files in an embedded dll

  5. Tony Says:

    Unfortunately no I have not. It has not been a requirement that I have had to worry about yet. When I was doing my research it showed up as an option.

  6. Amin Says:

    Thank you veeeeeeeeery much.
    It was really great.

Leave a Reply