random .NET and web development musings

I’ve used two approaches to “embedding” the ascx views, one thats quite simple, and one thats a bit more complicated and needs quite a lot of explanation as to why I’m doing it that way.

The easiest way to do it is to embed the ascx file, and access it with a Custom VirtualPathProvider.

This approach however suffers several drawbacks.

The first problem comes at dev time. You have to rebuild every time you change the ascx. This might not be a problem for you, but in large projects it can add several seconds delay to every change you make, which for someone whos trying to be productive, can be very annoying.

The easiest solution to this is to modify the custom VirtualPathProvider above so that it is aware whether it is in dev or production mode. I do this with the debug attribute in the web config:

public bool AreInDevMode
{
	get
	{
		var section = ConfigurationManager.GetSection("system.web/compilation") as CompilationSection;
		return section.Debug;
	}
}

Then, when you’re in dev mode, check the file system before checking for the embedded resource. This will perhaps involve some path hackery, but generally I’m ok with this because the path conventions in my projects are consistent.

The second problem with this method is that the view is essentially embedded as a string, the text that makes up the view ascx. This means the view has to be compiled at runtime. This has two issues:

a) its slow, especially when you have many views
b) you dont get build time compilation error checking.

So how do we solve this?

To compile the views, you need the ASP.NET Precompilation tool.

This will create a separate assembly for each compiled view. This is messy, so we can merge them together using the ASP.NET Merge tool.

Getting better, but you still have two assemblies, your main project code and the one with the views in. ILMerge to the rescue. However, before we do that I want to sort out the compiled view types.

If you open a precompiled view dll in reflector, you’ll find its rather messy and things aren’t named very nicely. For example, say your project has the following views:

/Plugins/PluginA/View-A.ascx
/Plugins/PluginB/View.ascx

You’ll end up with the following types:

plugins_plugina_view_a_ascx
plugins_pluginb_view_ascx

So, what I wan’t to do is put them back into the correct namespace. Unfortunately, because certain characters that are allowed in file paths are not allowed in namespaces, these chars get converted to underscores and you therefore can’t work out the namespace you need from the compiled type name alone. (e.g. you dont know if “path_to_some_view_ascx” was originally “path/to/some/view.ascx” or “path/to-some.view.ascx“)

Therefore I iterate over the file system and look for any .ascx, .aspx and .master files, turn their paths into their corresponding type names. I then have a lookup table of the type name and its original path.

THEN I can use Mono.Cecil to alter the type names.

so for the file:

/Plugins/PluginA/View.ascx

I calculate its type name:

plugins_plugina_view_ascx

and the name(space) I want it to have:

Plugins.PluginA.View

Do this for all the views and then I can ILMerge my two assemblies into one, nicely organised final package 😀

So how do you then get MVC to render the view?

First you need to extend WebFormsViewEngine, and override:

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
    return new WebFormView(partialPath, null);
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
    return new WebFormView(viewPath, masterPath);
}

So that it returns a custom View, which extends WebFormView and overrides Render:

public virtual void Render(ViewContext viewContext, TextWriter writer)

so that instead of creating the view using the BuildManager, it simply instantiates the type you worked out above.

If you followed any of that, well done!

3 COMMENTS
Victor Kornov
September 6, 2011
ad

You seem to have too much free time on your hands at work! 😉

Well done, nevertheless!

September 7, 2011
ad

Ha! Thanks :) Its saves me loads of time.

Hady El Haddad
May 22, 2014
ad

Could you please provide us with a running sample project because we did not understand how to implement the above in our solution.
Thank you in advance

Post a comment