Scott Hanselman

The Weekly Source Code 30 - VB.NET with XML Literals as a View Engine for ASP.NET MVC

July 3, '08 Comments [12] Posted in ASP.NET | ASP.NET MVC | VB | XML
Sponsored By

I was literally in the middle of writing the post when I saw a message from Andrew Davey about how he had implemented the same idea! Of course, his is way better, so I got to code via subtraction. That means subtracting out the crap I had written in a few minutes and dig into his code.

There are no unique ideas, right? ;) Either way, it's fun when the same idea is being thought about simultaneously.

Here's the general concept. A few weeks back I was talking with Avner Aharoni, a Language Program Manager, and he had been kicking around the idea of VB9's XML Literals making friendlier Views within ASP.NET MVC.

I've blogged about VB9's rocking sweet XML support before. It lets you create XML like this. Note the lack of strings...the XML is there in the language and the compiler and intellisense are all aware of it.

 Dim books = <bookstore xmlns="http://examples.books.com">
<book publicationdate=<%= publicationdate %> ISBN=<%= isbn %>>
<title>ASP.NET Book</title>
<price><%= price %></price>
<author>
<first-name><%= a.FirstName %></first-name>
<last-name><%= a.LastName %></last-name>
</author>
</book>
</bookstore>

Views in ASP.NET MVC

Starting with the Northwind ASP.NET MVC Sample Code for Preview 3 that Phil updated, let's look at the List View:

<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.CategoryName %></h2>
<ul>
<% foreach (Product product in ViewData.Model.Products.Model) { %>
<li id="prod<%= product.ProductID %>">
<%= product.ProductName %>
<span class="editlink"">
(<%= Html.ActionLink("Edit", new { Action="Edit", ID=product.ProductID })%>)
</span>
</li>
<% } %>
</ul>
<%= Html.ActionLink("Add New Product", new { Action="New" }) %>
</asp:Content>

This is straight from the ASPX page with inline C#.

ASP.NET MVC Views using VB9's XML Literal Support

imageNow, we thought it'd be cool/interesting/potentially-something if we could use the XML Literal support to get, as Andrew puts it "compiled, strongly typed, intellisense friendly views." Sure, we mostly get that with the ASPX pages, but perhaps this would be better/easier/something? Keep in mind here that we're playing.

Your opinions on if this is a good idea or something to move forward would be useful. Leave comments and I'll compile and give them directly on to the VB and MVC team(s)! Remember, it can look like however you think it should, so don't be constrained by my spike or Andrew's.

Here's why Andrew thinks it's cool, quoted from a post on the ALT.NET mailing list:

Some key features I've used.
- <%= From ... Select ... %> to create repeated elements
- VB's ternary "If" operator for conditional output
- X-linq to post process the HTML before sending it
- choose between indented and compressed XML output
- modules of functions as "controls" - it's so simple :)

Here's what my solution looks like in Visual Studio. See how the ListVB.aspx has no "+" sign. There's no code-behind .cs file there even though this is a C# project. The meat of the View is in another assembly (although you could conceivably do something freaky and get VB and C# to live in the same assembly (be sure to read the comments)).

Actually, the ListVB.aspx file is VB, not C# and refers not to to a code-behind, but another class in another DLL, specifically the VBViews assembly.

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
AutoEventWireup="true" Inherits="VBViews.ListVB" Title="Products" %>
<%@ Import Namespace="NorthwindModel" %>
<%@ Import Namespace="System.Collections.Generic" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.Model.CategoryName%></h2>
<% = GetList() %>
</asp:Content>

Here's the Visual Basic code in the other assembly.

Imports System.Xml.Linq
Imports NorthwindDemo.Models

Partial Public Class ListVB
Inherits System.Web.Mvc.ViewPage

Function GetList() As XElement
Dim c As Category = CType(ViewData.Model, Category)
Return <ul><%= From product In c.Products _
Select _
<li id=<%= "prod" & product.ProductID %>>
<span class="editlink">
<a href=<%= "/Products/Edit/" & product.ProductID %>>
<%= product.ProductName %>
</a>
</span>
</li> %>
</ul>
End Function
End Class

This won't really be clear without some syntax highlighting to make the point, so here it is again, but this time as a screenshot. See now the VB code, XML and <% %> blocks are all together in same line? VB is just generating XElement's which in turn will turn into a string when "rendered" by the ASPX page.

The same VB9 code as before, except this time in color.

Andrew's Take #2 on the VB9 XML Literals as ASP.NET MVC Views

Andrew Davey's NRest project is more than just VB9 Views. It's a REST web framework for ASP.NET using the Nemerle programming language (using the May CTP). You can browser or GET his code with SVN here: http://svn.assembla.com/svn/nrest/trunk/. It's also a nicely laid out solution that uses the Ninject IOC but I'll cover that later. Do check out Andrew's screencast about his NRest project.

His code is a mix of C#, Nemerle and VB. The Website, Tests and Services are in C# and the Ninject modules are in Nemerle, along with the meat of the main NRest project. I think he could have used more of System.MVC, specifically the View Engines, that he did, but I'm still grokking his intent.

He's got a hierarchy in VB with a MainPageBase, Page, in order to achieve a kind of Master Pages:

Public Class MainPageBase(Of TChrome As MainChrome, TContent)
Inherits Page(Of TChrome, TContent)

Public Overrides Function GetHtml() As XElement
Return _
<html>
<head>
<title><%= Chrome.Title %></title>
<link href="/styles/demo.css" type="text/css" rel="Stylesheet"/>
<%= GetHeadContents().Elements() %>
</head>
<body>
<h1><%= Chrome.Title %></h1>
<%= GetBodyContents().Elements() %>
</body>
</html>
End Function

Public Overridable Function GetHeadContents() As XElement
Return <_></_>
End Function

Public Overridable Function GetBodyContents() As XElement
Return <_></_>
End Function

End Class

So a Hello World page in VB would be very simple, just this:

Public Class CustomerPage
Inherits MainPageBase(Of CustomerPageData)

Public Overrides Function GetBodyContents() As XElement
Return _
<_>
<p>Hello <%= Content.FirstName & " " & Content.LastName %></p>
</_>
End Function
End Class

All of this is a work in progress, but it's really cool that we're all trying to push the envelope and not afraid to try crazy stuff in order to make things better. It'll be cool for me to read this post in a year and either say "ew" or "cool!" depending on what direction we all went.

Have you done anything cool or crazy with Views and ViewEngines, Dear Reader?

Related Posts

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, July 03, 2008 2:52:15 AM UTC
I say "cool!" for trying it, and "ew" to the idea of actually using it ;)

I think in almost every scenario, the "inline c#" (or VB) solution will have less "noise" than this solution.

I see the VB XML literal feature as a way to sprinkle in a touch of XML/HTML in an ocean of VB.
While inline C#/VB is a way to sprinkle in a touch of C# in an ocean of HTML.

Your views are likely to be dominated by HTML, so it makes sense to use an engine that does HTML well, while letting you sprinkle in a bit of code.
Thursday, July 03, 2008 3:27:47 AM UTC
I find it a bit worrying that nobody is actually thinking about how XLinq always means you're unnecessary duplicating your object models in-memory. When you do that fancy from... select new XElement, you're basically constructing a clone of your projected model but with a different set of objects.

With the aspx+inline C# approach, you don't duplicate the model, as under the covers it's all just iterators and Response.Write. That's better.

I think a nicer alternative would be something like the a href="http://www.codeplex.com/MVPXML/Thread/View.aspx?ThreadId=7193">Typed Templates</a> (if only I had some spare time to put it together for ASP.NET MVC...
Thursday, July 03, 2008 4:14:02 AM UTC
I think you both have a good point. I think that it'd be interesting to make an NVelocity-style ViewEngine that was more like what Daniel is saying but that only allows the production of XML/XHTML.
Scott Hanselman
Thursday, July 03, 2008 5:27:22 AM UTC
Now think about using the same idea to deliver RSS or Atom or JSON or anything else you need back simply and easily without the need for n API. This is where XML Literals really shines. Wanna build your own Astoria style interface, almost trivial with XML Literals.
Thursday, July 03, 2008 5:50:38 AM UTC
I always thought that PHP pages are very messy but after seeing in this post how a view looks like, I'm wondering - Is Microsoft going in the same direction?
Thursday, July 03, 2008 6:09:46 AM UTC
Actually, no. The interesting thing is that it's up to us to decide what a View SHOULD look like. What do you think one should look like?
Scott Hanselman
Thursday, July 03, 2008 8:31:50 AM UTC
Thanks Scott! I'm glad that people are thinking about new and different ways to make web programming better. There are so many tools and technologies out there!

The XLinq stuff will indeed eat more memory than a pure streaming model. However the benefit of post processing may be worth it. I need to look into using XStreamingElement as well, since this defers the evaluation of the XML. Also, take a look at using PLINQ to generate XElements in parallel...

I'll get some screencasts up soon that explain more about what I'm trying to achieve.
Thursday, July 03, 2008 9:58:11 AM UTC
Hi Scott,

I've a question on how you're connecting the ListVB.aspx page with the VBViews.ListVB class using the Inherits attribute. I have a ASP.NET (3.5) web application with the UI layout and style and some other options driven by the url the site was called from. So what my site does on calling is selecting the correct record from database by the url and creating the context from that record. the context contains the site language and which set of information to display.

Also the UI will be choosen by the record the way that there exist one theme and masterpage for each record, named by the record's ID. So the masterpages are named _1.master, _2.master etc. The masterpage contains some logic in a class DefaultMaster which inherits from the ASP.NET MasterPage class and all of my _<ID>.master masterpages have to inherit from that class. But because of the partial class model my class in the .master files doesn't work - the compiler would try to generate one class from DefaultMaster, _1.master, _2.master etc. Therefore I have to create an empty class for each new masterpage inheriting from DefaultMaster, which means that I have to recompile the whole site and also restart the productional web application when adding a new customer.

What I want to do is to copy only the new .master file, theme folder and some other static files (in App_Data) to the server and set up the new records in the database without the need of touching either the bin folder or the web.config file. Is this possible with the Inherits attribute and how exactly to do this?

Thanks in advance,

Marco
Marco
Thursday, July 03, 2008 10:59:49 AM UTC
I like Andrew's idea of a masterpage where you inherit form a class and override methods to provde content. Really ties together the idea of a masterpage with OOP. Almost too smart!

But I really do not like the fact that there's now HTML (XML really) in a .vb file separate from the view. Maintenance nightmare I think. Why can't the XLinq code of the GetList() method be used in the .aspx view? That would be better I think?
Mike
Thursday, July 03, 2008 3:18:23 PM UTC
I think it's a great idea up until someone puts all of their xml/html into the vb file. If people only use it for dynamic things like populating a dropdown list with items from a database then that would be fine. I don't know what the benefit would be other than the strong typing though. Most of what this adds already exists with functions built in to the controls you are using.
Preston
Thursday, July 03, 2008 3:22:26 PM UTC
@Scott

We tried this when VS 2008 first came out, I agree it's cool. We used a VB class library instead of aspx. It was a trick to get a VB and C# in one solution, dont remember the exact issue but we had some.

We stopped because of the VB XML Performance, Huge XML Node Trees.

We then switched to precompiled aspx pages that never spin up the Page object or any web controls. It's a hack, but it's very fast. In the aspx page simply close out the generated render method with a } Then create a static type save render method that accepts a writer and the model. Having a static view method is pretty cool, you can also place multiple views in one aspx page if needed. It's always precompiled strong typed as you never use a string "MyView.aspx", instead ViewName.Render(writer, model).

There are some other variations on this, to get to context and also inject js, head info etc...
Mike D
Friday, August 01, 2008 5:35:59 AM UTC
Your link to your post on rocking sweet exampls of VB9 XML literals doesn't work.
John "Z-Bo" Zabroski
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.