Question

I'm looking to implement some templates in a web application and wanted to utilize ASP.NET's templated controls. However, I don't want to rely on physical .ascx files, or the VirtualPathProvider in order to Load the templates.

I want to be able to load the templates from a database or other datastore (in memory?). Is there any implementation of a LoadTemplate() method that returns an ITemplate given a string representation of an .ascx template?

If not, how would I go about writing one?

FYI, Kentico has a similar feature, but they rely on the VirtualPathProvider in order to use the LoadTemplate() on the TemplateControl class. With that method, they are able to load templates (they call them transformations) stored in the database.

Was it helpful?

Solution

Yes, VirtualPathProvider is probably the method you will want to use, if a string or database is the source you want to use. (There are also code generators that can emit code, but usually those are used when building the code dynamically--not loading from an external source as in your case.)

You don't mention why you don't want to use the VirtualPathProvider though. Is it due to not wanting to, or can't because of some special requirements you have in a particular situation?

Finally, if it "seems trivial" to load and compile code dynamically, then you don't know what all the whole .Net system has to do before it can run dynamic code--assembly generation, compilation and JIT, application contexts, class/member name resolution, code security, etc. Maybe you've just been spoiled with how easy .Net has made doing other complicated tasks. ;-)

OTHER TIPS

I've been facing a similar problem. However, the VirtualPathProvider is just too much plumbing to implement for such a small gain - not to mention that it seems like it has the potential to be a bit risky security-wise to implement. I've found two possible work-arounds:

1) Use reflection to get at what you want:

var page = HttpContext.Current.Handler as Page;
string text = "<table><tr><td>Testing!!!</td></tr></table>";
var systemWebAssembly = System.Reflection.Assembly.GetAssembly(typeof(Page));
var virtualPathType = systemWebAssembly.GetTypes().Where(t => t.Name == "VirtualPath").FirstOrDefault(); // Type.GetType("System.Web.VirtualPath");
var createMethod = virtualPathType.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).Where(m => m.Name == "Create" && m.GetParameters().Length == 1).FirstOrDefault();
object virtualPath = createMethod.Invoke(null, new object[]
{ 
    page.AppRelativeVirtualPath 
});
var template = (ITemplate)typeof(TemplateParser).GetMethod("ParseTemplate", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).Invoke(null, new object[]{text, virtualPath, true});

2) Use a somewhat hacky work-around:

var page = HttpContext.Current.Handler as Page;
string text = "<table><tr><td>Testing!!!</td></tr></table>";
string modifiedText = string.Format("<asp:UpdatePanel runat=\"server\"><ContentTemplate>{0}</ContentTemplate></asp:UpdatePanel>", text);
var control = page.ParseControl(modifiedText);
var updatePanel = control.Controls[0] as UpdatePanel;
var template = updatePanel.ContentTemplate;

I openly admin that neither is a great solution. Ideally, there would be a method in the .Net Framework for this sort of thing. Something like:

public class TemplateParser
{
    public static ITemplate ParseTemplate(string content, string virtualPath, bool ignoreParserFilter)
    {
        return TemplateParser.ParseTemplate(string content, VirtualPath.Create(virtualPath), ignoreParserFilter);
    }
}

That would alleviate the whole need to implement the VirtualPathProvider. Maybe we'll see that in ASP.NET vNext :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top