Scripting ASP.NET MVC Views Stored In The Database

code, asp.net mvc, dlr 0 comments suggest edit

Say you’re building a web application and you want, against your better judgment perhaps, to allow end users to easily customize the look and feel – a common scenario within a blog engine or any hosted application.

With ASP.NET, view code tends to be some complex declarative markup stuck in a file on disk which gets compiled by ASP.NET into an assembly. Most system administrators would first pluck out their own toenail rather than allow an end user permission to modify such files.

It’s possible to store such files in the database and use a VirtualPathProvider to load them, but that requires your application (and thus their views) to run in full trust. Is there a way you could safely store such views in the database in an application running in medium trust where the code in the view is approachable?

At the ALT.NET conference a little while back, Jimmy Schementi and John Lam gave a talk about the pattern of hosting a scripting language within a larger application. For example, many modern 3-D Games have their high performance core engine written in C++ and Assembly. However, these games often use a scripting language, such as Lua, to write the scripts for the behaviors of characters and objects.

An example that might be more familiar to more people is the use of VBA to write macros for Excel. In both of these cases, the larger application hosts a scripting environment that allow end users to customize the application using a simpler lighter weight language than the one the core app is written in.

A long while back, I wrote a blog post about defining ASP.NET MVC Views in IronRuby followed by a full IronRuby ASP.NET MVC stack. While there was some passionate interest by a few, in general, I was met with the thunderous sound of crickets. Why the huge lack of interest? Probably because I didn’t really sell the benefit and the explain the pain it solves. I’m sure many of you were asking, Why bother? What’s in it for me?

After thinking about it some more, I realized that my prototypes appeared to suggest that if you want to take advantage of IronRuby, you would need to make some sort of wholesale switch to a new foreign language, not something to be undertaken lightly.

This is why I really like Jimmy and John’s recent efforts to focus on showing the benefits of hosting the DLR for scripting scenarios like the ones mentioned above. It makes total sense to me when I look at it in this perspective. The way I see it, most developers spend a huge bulk of their time in a single core language, typically their “language of choice”. For me, I spend the bulk of my time writing C# code.

However, I don’t think twice about the fact that I also write tons of JavaScript when I do web development, and I’ll write the occasional VB code when I need a new Macro for Visual Studio or Excel. I also write SQL when I need to. I’m happy to pick up and use a new language when it will enable me to do the job at hand more efficiently and naturally than C# does. I imagine many developers feel this way. The occasional use of a scripting languages is fine when it gets the job done and I can still spend most of my time in my favorite language.

So I started thinking about how that might work in a web application. What if you could write all your business logic and controller logic in your language of choice, but have your views written in a light weight scripting language. If my web application were to host a scripting engine, I could actually store the code in any medium I want, such as the database. Having them in the database makes it very easy for end users to modify it since it wouldn’t require file upload permissions into the web root.

This is where hosting the DLR is a nice fit. I put together a proof of concept for these ideas. This is just a prototype intended to show how such a workflow might work. In this prototype, you go about creating your models and controllers the way you normally would.

For example, here’s a controller that returns some structured data to the view in the form of an anonymous type.

public ActionResult FunWithScripting()
{
  var someData = new { 
    salutation = "Are you having fun with scripting yet?", 
    theDate = DateTime.Now,
      numbers = new int[] { 1, 2, 3, 4 } 
  };

  return View(someData);
}

Once you write your controller, but before you create your view, you compile the app and then go visit the URL.View does not exist
view

We haven’t created the view yet, so let’s follow the instructions and login. Afterwards, we this:

view
editor

Since the view doesn’t exist, I hooked in and provided a temporary view for the controller action which contains a view editor. Notice that at the bottom of the screen, you can see the current property names and values being passed to the view. For example, there’s an enumeration of integers as one property, so I was able to use the Ruby each method to print them out in the view.

The sweet little browser based source code editor is named Edit Area created by Christophe Dolivet. Unfortunately, at the time I write this, it doesn’t yet have support for ERB style syntax highlighting schemes. That’s why the <% and %> aren’t highlighted in yellow.

When I click Create View, I get taken back to the request for the same action, but now I can see the view I just created (click to enlarge).

Fun with scripting
view

In the future, I should be able to host C# views in this way. Mono already has a tool for dynamically compiling C# code passed in as a string which I could try and incorporate.

I’m seriously thinking of making this the approach for building skins in a future version of Subtext. That would make skin installation drop dead simple and not require any file directory access. Let me know if you make use of this technique in your applications.

If you try and run this prototype, please note that there are some quirky caching issues with editing existing views in the prototype. It’ll seem like your view is not being edited, but it’s a result of how views are being cached. It might take a bit of time before your edits show up. I’m sure there are other bugs I’m still in the process of fixing. But for the most part, the general principle is sound.

You can download the prototype here.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

45 responses

  1. Avatar for Andrea Balducci
    Andrea Balducci April 22nd, 2009

    Great! I'm currently using VirtualPathProvider to embed views and controllers in separate assemblies and dynamically load them at application startup (so i can can mix mvc apps in a portal web app just by xcopy deployment) and I was thinking about adding dynamic view creation in a future version of my framework. I'll try your prototype as soon as i can, just loving this approach!
    Thanks for sharing!

  2. Avatar for Shiju Varghese
    Shiju Varghese April 22nd, 2009

    Phill, Awesome post.

  3. Avatar for Vijay Santhanam
    Vijay Santhanam April 22nd, 2009

    Cool! Your zip is missing MemberPropertyInfo.cs

  4. Avatar for Haacked
    Haacked April 22nd, 2009

    @Vijay thanks for the catch! I forgot to svn add that file before I svn exported the project. In other words, my bad!

  5. Avatar for Jonathan Adams
    Jonathan Adams April 22nd, 2009

    I have been doing something similar with a standard web forms using nvelocity. This would be a much better fit especially now I making the hike over to MVC , excellent post thanks phil !

  6. Avatar for Dan Elliott
    Dan Elliott April 22nd, 2009

    Fantastic article, Phil! Thank you :)
    I look forward to giving it a whirl.
    Kindness,
    Dan

  7. Avatar for Maarten Balliauw
    Maarten Balliauw April 22nd, 2009

    Sounds like a great way to embed view inside my plugin assemblies (blog.maartenballiauw.be/.../ASPNET-MVC-and-the-...(MEF).aspx)
    Do you have any idea what this means to performance?

  8. Avatar for Charlie M
    Charlie M April 22nd, 2009

    Fantastic! Excellent post and exactly what I was looking for.
    ASP.NET MVC + a dynamic language = a pretty decent CMS offering!

  9. Avatar for Vijay Santhanam
    Vijay Santhanam April 22nd, 2009

    ModelProperties.ascx is missing too

  10. Avatar for Andy
    Andy April 22nd, 2009

    Am I right that in ASP.NET 4.0 that VirtualPathProvider will work in Medium Trust (I'm sure this was mentioned a month back on one of the ASP.NET team member's blogs, but have lost the link!)? Of course, your approach will help for hosts with older .NET installs.

  11. Avatar for huey
    huey April 22nd, 2009

    I really hate having to make ViewModels (I try and just pass Business Object but it doesn't always fit). It can become annoying when you start to get a ton of ViewModels that offer no benefit as far as I can tell -- just clutter.
    I'm not really dynamic language smart, and maybe you can do this with C#, but passing an anonymous type to the View seems awesome. No need to create a model just for the hand off from the controller to the view.
    Is this only available with dynamic languages? I wouldn't want to use a dynamic language for my business logic (I'm comfortable with C#) but for the view it seems to have benefits.

  12. Avatar for Andy
    Andy April 22nd, 2009

    Update - the post that suggests VirtualPathProviders and BuildProviders will work in Medium Trust in ASP.NET 4.0 is weblogs.asp.net/.../...t-compiler-black-magic.aspx.

  13. Avatar for Chris Cyvas
    Chris Cyvas April 22nd, 2009

    I was just looking for something like this last week - excellent!

  14. Avatar for Page Brooks
    Page Brooks April 22nd, 2009

    I think Graffiti CMS does something similar to this using Chalk.

  15. Avatar for HB
    HB April 22nd, 2009

    @huey
    I hear what you're saying - I've written a class called AnonymousType that hides all the complexities of Reflection and lets me use simpler methods like something.Get<int>("Total").
    You could probably write your own pretty easily, or at least until the new dynamic comes out.

  16. Avatar for Dody Gunawinata
    Dody Gunawinata April 22nd, 2009

    Override the caching lookup in VirtualFile to zero so you will not have a cache issue. The problem is that you are pretty much recompiling the view on the fly for every request.

  17. Avatar for Haacked
    Haacked April 22nd, 2009

    Yep, in .NET 4.0, VirtualPathProvider will run in medium trust. Once that happens, I may be able to swap out my approach to use VPP combined with DLR support for ASP.NET. Or I could use Spark in that case, which also supports IronRuby and IronPython.
    As for performance, it's probably not faster than a fully compiled view, but with proper caching, it should hopefully not be a problem on a real site.

  18. Avatar for john
    john April 22nd, 2009

    is there any chance to get a run through of the code. I downloaded it, i might be new to this whole thing but it looks like you are using a virtualpathprovider still. Although I can't open all your projects with my version of visual studio.

  19. Avatar for Guillaume
    Guillaume April 23rd, 2009

    As far as I know BRail is using this kind of approach using Boo as a scripting language. I don't know if someone tryed a DB implementation of it yet.
    Does anyone have some interesting links about BRail ?

  20. Avatar for james
    james April 23rd, 2009

    Quote from "http://www.lua.org/about.html"
    'Please do not write it as "LUA", which is both ugly and confusing, because then it becomes an acronym with different meanings for different people. So, please, write "Lua" right!'
    ;-)

  21. Avatar for Farrio
    Farrio April 23rd, 2009

    OMG! COOL!

  22. Avatar for Haacked
    Haacked April 23rd, 2009

    @Huey all our built in helpers work with anonymous types. If you call <%= Html.Eval("PropertyName") %> we'll do reflection on Model object to grab that value.

  23. Avatar for Koistya `Navin
    Koistya `Navin April 23rd, 2009

    Great finding. Going to use this approach in my apps.

  24. Avatar for Daniel Plaisted
    Daniel Plaisted April 24th, 2009

    "Mono already has a tool for dynamically compiling C# code passed in as a string which I could try and incorporate."
    So does vanilla .NET. See http://support.microsoft.com/kb/304655, or the CSharpCodeProvider class.

  25. Avatar for zihotki
    zihotki April 25th, 2009

    Daniel,
    CSharpCodeProvider can be used only in FullTrust environment. But the main goal of this article is to show to make this working in MediumTrust too. So CSharpCodeProvider can not be used here.

  26. Avatar for bradvin
    bradvin April 29th, 2009

    awesome post!
    "Mono already has a tool for dynamically compiling C# code"
    how would you go about using this tool from your code?

  27. Avatar for Haacked
    Haacked May 3rd, 2009

    @bradvin I think it's just a library you reference. You can go to the Mono site and look around. Maybe ask someone in the forums. I've never used it. I was just told about it.

  28. Avatar for Eric Sims
    Eric Sims May 13th, 2009

    Phil, did you ever get the caching bug figured out? It seems that you have to override the CacheDependency methond (like Dody said) (at least in the standard VPP implementation).
    I think the default is that it creates a cache in the 'Temporary ASP.NET Files' folder and keeps it there for a default of 20 minutes.
    My problem is that I'm having difficulty invalidating that cache based on a trigger of some sort (file dependency, etc).
    I can, of course, delete those files manually, but that's no use in the real world.
    Please let me know once you have this figured out.

  29. Avatar for Bret (RunXc)
    Bret (RunXc) May 19th, 2009

    I am curious. Why did you use IronRuby instead of IronPython?? I have been thinking of learning a bit about the DLR but I am not sure which Language to look at first.

  30. Avatar for Eric Sims
    Eric Sims May 19th, 2009

    i figured out in that edit_area editor how to make the syntax highlight. it's pretty easy actually.
    you just create a new .js file (copy a similar existing one like html.js) and add your own highlight logic and instead of doing a 'color: #ff0000', you just do a 'background-color: #ff0000'.
    hope this helps somebody.

  31. Avatar for Haacked
    Haacked May 19th, 2009

    @Bret I've used Ruby in the past (with Rails for example) and I'm more familiar with it than Python. That's all.

  32. Avatar for David Robbins
    David Robbins May 26th, 2009

    Very cool. In a sense it is similar to microtemplates but processed server side.

  33. Avatar for Ian Mckay
    Ian Mckay January 24th, 2010

    Hi Phil. Great post! This is exactly what I've been looking for.
    When I try to download the source I'm getting a corrupted zip file error in win 7 explorer and winrar.

  34. Avatar for Lars
    Lars January 25th, 2010

    Hello!
    I am getting the same Zip file error. Any chance of building a new zip file?

  35. Avatar for Ed
    Ed February 16th, 2010

    Anxious to see it...but file is corrupt

  36. Avatar for Kieron Lanning
    Kieron Lanning February 28th, 2010

    Hi Phil, as some people have reported, the zip file is corrupt, think you could fix/ post an update?
    Thanks,
    Kieron

  37. Avatar for silky
    silky March 5th, 2010

    Wow. This has got to be one of the worst ideas I've read about in a while. I'll be honest, I've read your site before (a long time ago) and decided that it wasn't to my liking, but promoting such a dynamic strategy in our language, and the numerous resulting issues that could come from this implementation (that you haven't raised) it really disturbs me. I know that won't mean anything, but I'm just fairly shocked. And it is even more disturbing that so many people seem to like it. I think someone such as yourself, who numerous people seem to respect, should *at the very least* comment on the downsides of such an implementation, because they are significant.
    I realise this was posted a while ago, so maybe you've commented on it since then; I hope so. Regardless, amongst the claims of joy and brilliance about this system, I'd like to express my disgust.

  38. Avatar for Tad
    Tad March 13th, 2010

    Phil , could you re upload that project ?:) please :):)

  39. Avatar for Adam
    Adam March 16th, 2010

    It's been almost a year since this was posted and I'm interested in doing this for an upcoming MVC 2.0 project, so I was wondering if anyone has tried using this technique for a real application. I posted a question on StackOverflow, so any help would be appreciated. Thanks!

  40. Avatar for Fred
    Fred April 7th, 2010

    the download link is broken.

  41. Avatar for Andrew Siemer
    Andrew Siemer July 28th, 2010

    Phil, this is exactly what I was hoping to find (without actually looking for it). I am under way creating a new multi-tenant CMS app (don't ask why) and was going to use NVelocity for dynamic view content in my views across multiple sites. Given that NVelocity hasn't been updated in years...I was really hoping for another language - and Ruby is super popular! Now I need to figure out a way to pull routes and controllers from the db and dynamically compile/load controllers... Thoughts on this last piece?

  42. Avatar for Matt
    Matt August 30th, 2010

    Any way you can put the code back up? Getting a file not found.

  43. Avatar for erwin
    erwin September 18th, 2010

    download link is broken, could you upload it again.
    Really need it right now.

  44. Avatar for haacked
    haacked September 22nd, 2010

    Hi All, I apologize for the broken download link. I just fixed it. Keep in mind, this is compiled against ASP.NET MVC 1.0.

  45. Avatar for W
    W April 19th, 2011

    Hey, are you going to continue this series? I'm now working on more complex than average views and I think something like this would work quite well for my situation.
    If I can use a higher level language without all the tediousness of using C# for my views while keeping the rest of my code the same, that would be great!