Blog

Embedding Pages and Usercontrols Inside Assemblies

One of the problems with ASP.Net when trying to keep a degree of separation between project components is that you can’t easily implement a page or a user control inside a library project and simply reference it from the main ASP.Net project.

This, I think, its not entirely by lack of vision or interest from the ASP.Net development team but instead by the complexity of the some issues of the technique like the possibility of overlapping file names (just like trying to mount 2 filesystems under the same folder), to name one.

This said, just let me add that my approach doesn’t solve that problem, but like most uncommon problems we are usually faced with uncommon manual steps and “check lists” before deploying. To a relatively small project of one with a nice amount of control over the libraries this can open nice possibilities specially for addin based development on ASP.Net. So, the code:

class AssemblyResourceVirtualFile : VirtualFile
{
protected string path;
public AssemblyResourceVirtualFile(string virtualPath) : base(virtualPath)
{
path = VirtualPathUtility.ToAppRelative(virtualPath);
}

public override Stream Open()
{
string[] parts = path.Split('/');
string assemblyName = parts[2] + ".dll";
string resourceName = parts[3];
assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
Assembly assembly = Assembly.LoadFile(assemblyName);

if (assembly != null)
return assembly.GetManifestResourceStream(resourceName);

return null;
}
}

public class AssemblyResourceProvider : System.Web.Hosting.VirtualPathProvider
{
public AssemblyResourceProvider() { }
private bool IsAppResourcePath(string virtualPath)
{
String checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
return checkPath.StartsWith("~/App_Resource/",
StringComparison.InvariantCultureIgnoreCase);
}

public override bool FileExists(string virtualPath)
{
return (IsAppResourcePath(virtualPath) ||
base.FileExists(virtualPath));
}

public override VirtualFile GetFile(string virtualPath)
{
if (IsAppResourcePath(virtualPath))
return new AssemblyResourceVirtualFile(virtualPath);
else
return base.GetFile(virtualPath);
}

public override System.Web.Caching.CacheDependency
GetCacheDependency(string virtualPath,
System.Collections.IEnumerable virtualPathDependencies,
DateTime utcStart)
{
if (IsAppResourcePath(virtualPath))
return null;
else
return base.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
}
}

So, as you can see my technique is based on VirtualPathProvider and in this case I implement a Assembly Resource based implementation. To properly use this you need to use

HostingEnvironment.RegisterVirtualPathProvider(new Web.Net.AssemblyResourceProvider());

on Application_Start of global.asax or App_Code folder as stated in the MSDN Page. There are some limitations, like you can’t include a global.asax or web.config file using this technique but you can read all of those in the MSDN Page… Still, it can reduce the number of files by a great number.

To use this code on runtime you can use Page.Load for user controls (.ascx) or type the correct url on the browser like this “~/App_Resource/AssemblyName/Namespace.Tests.aspx”. In order to correctly work on the browser, the Assembly name is the filename without the “.dll” – else IIS would try to run the dll and pass the rest as args.

Post to Twitter Post to Delicious Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

Tags: , , ,

One Response to “Embedding Pages and Usercontrols Inside Assemblies”

  1. Joe Audette says:

    Hi Alexandre,

    Interesting technique!
    One thing to be aware of though, is
    that use of custom VirtualPathProvider doesn’t work in Medium Trust hosting environments which means this technique will not work in a lot of web hosting because more and more of them enforce Medium
    Trust these days.
    I’m not sure but loading assemblies might also not work in Medium Trust, as use of Reflection is very limited in Medium Trust.
    Still, its a useful technique if you have full
    control of the server.

    Best Regards,

    Joe

Leave a Reply

For spam filtering purposes, please copy the number 8043 to the field below: