using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Reflection; using System.IO; using System.Web.Hosting; using System.Web.Caching; using System.Collections; using System.Diagnostics; namespace HLWebPluginHost.PluginLib { /// /// The VirtualPathProvider class provides developers a way to intercept the calls MVC makes to /// retrieve files from the filesystem and provide these files. In our case we look at the plugins /// directory to find out if it provides the requested ressource. /// In this case the data is pulled out of the plugin and returned if it was a file. /// In Fact this class provides parts of the routing and dispatching mechanism known from other mvc frameworks. /// public class AssemblyResourceProvider : System.Web.Hosting.VirtualPathProvider { /// /// FileExists() is called by the framework to check for the existence of a physical file. /// We want to override the method so we can check for the “~/Plugins/” string in our path. /// If we find it, then we use reflection to determine if we have an embedded view in the targeted assembly that matches the requested file name. /// An example of a parameter that would be passed into this method would be “~/Plugins/FzySqrPlugin.dll/FzySqrPlugin.Views.HelloWorld.Index.aspx”. /// Given this path for a view, this code would load the FzySqrPlugin.dll and look for a resource with named FzySqrPlugin.Views.HelloWorld.Index.aspx. /// If the”~/Plugins/” string is not found, then the super class’s FileExist() method is called and the normal behavior takes place. /// /// /// public override bool FileExists(string virtualPath) { if (IsAppResourcePath(virtualPath)) { string path = VirtualPathUtility.ToAppRelative(virtualPath); string[] parts = path.Split('/'); string assemblyName = parts[2]; string resourceName = parts[3]; assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName); byte[] assemblyBytes = File.ReadAllBytes(assemblyName); Assembly assembly = Assembly.Load(assemblyBytes); if (assembly != null) { string[] resourceList = assembly.GetManifestResourceNames(); bool found = Array.Exists(resourceList, delegate(string r) { return r.Equals(resourceName); }); return found; } return false; } else return base.FileExists(virtualPath); } /// /// If we have a plugin path, then return our custom AssemblyResourceVirtualFile type, /// otherwise provide the standard behavior. /// /// /// public override VirtualFile GetFile(string virtualPath) { if (IsAppResourcePath(virtualPath)) { return new AssemblyResourceVirtualFile(virtualPath); } else { return base.GetFile(virtualPath); } } /// /// If we have a plugin path, return null (for now), otherwise invoke the super class’s method. /// /// /// /// /// public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) { if (IsAppResourcePath(virtualPath)) { return null; } return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } /// /// check a virtual path and decide whether the resource is provided by the plugin or the plugin host. /// To do this, we use the convention that all paths destined for plugin modules will begin with “~/Plugins/”. /// In other words, the path ~/Plugins/MyPlugin.dll/SomeResource.aspx indicates that the resource it represents /// is not a physical file and needs to be loaded from the MyPlugin.dll. /// /// /// True if a ressource at a given path is an app ressource private bool IsAppResourcePath(string virtualPath) { String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return checkPath.StartsWith("~/Plugins/", StringComparison.InvariantCultureIgnoreCase); } } }