
In this recipe, we will take a look at a different view engine for the ASP.NET MVC framework called NVelocity. NVelocity is a very stable templating engine that has been around longer than the MVC framework has. It plugs into, and works quite nicely with, the ASP.NET MVC framework (as we will see). In this particular recipe, we will use NVelocity in a slightly different manner than just wiring in a different view engine. We will use NVelocity to render views directly from a database. This gives us templating abilities for rendering dynamic views, but this could also be used to render the contents of an e-mail, or any other template-driven blob of text.
As in the other recipes, we will start off with the ViewModel
recipe, discussed earlier. Next, we need to grab a copy of the NVelocity library at http://www.castleproject.org/castle/projects.html. (I also put the library in the dependencies folder). You will need to have SQL Server Express installed to follow along with this recipe.
- The first step to this project could be a number of things! Let's get started by adding a reference to the NVelocity library (
NVelocity.dll
) in the dependencies folder. - Next we need to set up a quick database to use in the steps ahead. Do this by right-clicking on the
App_Data
folder in your project and select Add a new item. Choose a SQL Server database. I named my databaseTemplates
. - Now in your Server Explorer window you should see under the Data Connections section a
Templates.mdf
entry. Expand that entry. Right-click on thetables
folder and add a new table.- Set the first column of this table, to be called
TemplateID
, with a data type ofint
. Set this column to be a primary key (by clicking on the key icon above). And then set the identity specification (Is Identity) to be Yes. - Then add another column, called TemplateContent, with a data type of
VarChar(MAX)
. - Add one more column called ViewName. This will be a
VarChar(100)
field. Save this table and call itTemplates
.
- Set the first column of this table, to be called
- Then we will add LINQ to SQL to our project. Do this by right-clicking on the
Models
folder and select Add new item. Select the LINQ to SQL Classes entry and name itTemplate.dbml
. - When the design surface opens, drag your
Template
table from the server explorer onto the design surface. Save that file and close it. Then build your project (this builds theTemplateDataContext
class for you, which we will need in the following steps). - Right-click on the
Templates.mdf
entry in the server explorer and select Properties. In the Properties window, select and copy the entire connection string entry. It should look something like this:Data Source=.\SQLEXPRESS;AttachDbFilename={driveLetter}:\{pathToFile}\Templates.mdf;Integrated Security=True;User Instance=True
- Now create a new class in the
Models
directory calledTemplateRepository.cs
. In this class, we will create a method calledGetTemplateByViewName
, which will be responsible for getting a template from the database by the template's name.Models/TemplateRepository.cs:
public class TemplateRepository { public string GetTemplateByViewName(string ViewName) { string connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename= {FilePathToDataBase}\Templates.mdf;Integrated Security= True;User Instance=True"; string result = ""; using(TemplateDataContext dc = new TemplateDataContext(connectionString)) { Template template = (from t in dc.Templates where t.ViewName == ViewName select t).FirstOrDefault(); result = template != null ? template.TemplateContent : "The requested template was not found for view " + ViewName; } return result; } }
- Now we can create a class that will work with NVelocity to parse our template. Right-click on the
Models
folder and add a new class calledTemplateService.cs
. This class will have a method calledParseTemplateByViewName
. This method will be responsible for interacting with the NVelocity view engine to parse a passed in template.Models/TemplateService.cs:
public class TemplateService { public string ParseTemplateByViewName(string template, Dictionary<string,object> viewParams) { VelocityContext vc = new VelocityContext(); foreach (var v in viewParams) { vc.Put(v.Key, v.Value); } //create the engine VelocityEngine ve = new VelocityEngine(); ve.Init(); //the output stream StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); //merge the template ve.Evaluate(vc, sw, string.Empty, template); return sb.ToString(); } }
- Now we need to add some code to our
HomeController
. Let's start by creating a new action calledAnotherProduct()
. Inside this action, we will still utilize the creation of aProduct
and aCategory
. And we will use ourViewModel
as our transport object, which will carry theProduct
andCategory
instance for us. Then we need to create a dictionary that will take the variables we used in the parsing of our template. Lastly, we will call ourTemplateService
to parse out the template that theTemplateRepository
locates for us.Controllers/HomeController.cs:
public ActionResult AnotherProduct() { Product p = Builder<Product> .CreateNew() .Build(); Category c = Builder<Category> .CreateNew() .Build(); ProductView pv = new ProductView(); pv.CurrentCategory = c; pv.CurrentProduct = p; //define the variable context Dictionary<string, object> variableParams = new Dictionary<string,object>(); variableParams.Add("ProductView", pv); ViewData["content"] = new TemplateService() .ParseTemplateByViewName(new TemplateRepository().GetTemplateByViewName("AnotherProduct"), variableParams); return View(); }
- Then you can create the
AnotherView
view. This will be an empty view that simply renders the content from withinViewData
.Views/Home/AnotherProduct.aspx:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <%= ViewData["content"] %>
- Lastly, before anything will actually come together, we need to add an entry into our database. The
TemplateContent
entry will look like this:<html><head><title>$ProductView.CurrentProduct.ProductName</title></head><body><p>$ProductView.CurrentProduct.ProductName</p>Hello world from an NVelocity template!</body></html>
The
ViewName
will beAnotherProduct
to match ourAnotherProduct
action. - Now you can start debugging by hitting F5 and see the results of our database-driven view.
This is a simple modification to the way that NVelocity would normally work. In most cases, you would store your NVelocity templates on the file system or in the resource files. These are then read by a file-based template reader and parsed similarly to the fashion defined earlier. All that we did differently was grabbed our content from the database.
We have still piggybacked our implementation of NVelocity on top of the MVC framework and most importantly on top of the WebForms view engine. This means that we could use regular views for most of our pages, while templates from the database could be scattered about where we may want to have user-generated or easily administered content.
Some people might complain about the choice to piggyback the NVelocity view engine on the back of the MVC framework and the WebForms view engine. However, think of the flexibility that you would have with this option when pulling your dynamic-view content from the database.
By using the WebForm's view engine on top of the MVC framework, you get all sorts of built-in features such as a master page, templated helpers, scaffolding support, and so on. Using the NVelocity engine you can then define the remainder of your content from the database. This means that rather than having a statically defined Product
partial view that all of the products in your shopping cart catalog use, you could instead have a different Product
layout for every product. Expand this thought a bit further so that you could call and parse many templates from the database for one view, allowing you to define NVelocity partial templates, such as a common Product
header and footer, attributes layout, and so on. And now that all of this layout logic is persisted to the database, it can all be edited from the Web.
If you are interested in replacing the default view engine (WebForms view engine) with NVelocity, have a look at the MVC Contrib (codeplex.com/MVCContrib
) project. The guys at Headspring (headspringsystems.com
) currently have various classes created that will allow you to override the MVC framework to use NVelocity as the primary view engine in the MvcContrib.ViewEngines.NVelocity.dll
(currently).
While researching this particular recipe, I came across all sorts of ways to work with NVelocity. One demo went that extra step to show multiple ways to load templates in a very clean fashion. I put this demo in the code directory (included with the source of this book). You can find the original posting here: http://mihkeltt.blogspot.com/2009/12/nvelocity-template-engine-sample.html.
Javier Lozano has been putting together a product called MVC Turbine. One of its goals is to allow you to host content from many view engines at once (among many other cool things). Take a look here for more on that topic: lozanotek.com/blog/archive/2009/10/05/Multiple_View_Engines_with_MVC_Turbine.aspx.