Monday, March 26, 2012

Creating Custom Asp.net MVC ViewEngine

In the last post I talked about creating a custom ActionResult, the next question is, what if I want to add my a custom view engine?   As an example lets say your company’s intranet uses multiple platforms (php, java, and now asp.net mvc) and to keep the look and feel constant you use xslt to render your header and footer, a simple solution is to add a xslt View Engine to generate the markup.

First you create a view to parse and output for content
   1: public class XsltView : IView
   2: {
   3:     private readonly XslCompiledTransform _template;
   4:  
   5:     public XsltView(ControllerContext controllerContext, string viewPath)
   6:     {
   7:         _template = new XslCompiledTransform();
   8:         _template.Load(controllerContext.HttpContext.Server.MapPath(viewPath));
   9:     }
  10:  
  11:     public void Render(ViewContext viewContext, TextWriter writer)
  12:     {
  13:         var xmlDoc = viewContext.ViewData.Model as XDocument;
  14:         if(xmlDoc==null)
  15:         {
  16:             var xs = new XmlSerializer(viewContext.ViewData.Model.GetType());
  17:             xmlDoc = new XDocument();
  18:             using (var xWriter = xmlDoc.CreateWriter())
  19:             {
  20:                 xs.Serialize(xWriter, viewContext.ViewData.Model);
  21:             }
  22:         }
  23:         _template.Transform(xmlDoc.CreateReader(),null,writer);
  24:     }
  25: }
then you create a view engine by inheriting from VirtualPathProviderViewEngine and override the CreatePartialView and CreateView methods to call your view
   1: public class XsltViewEngine : VirtualPathProviderViewEngine
   2: {
   3:     protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
   4:     {
   5:         return new XsltView(controllerContext, partialPath);
   6:     }
   7:  
   8:     protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
   9:     {
  10:         return new XsltView(controllerContext, viewPath);
  11:     }
  12: }

to add the view open global.ascx.cs and modify the application start method to add the new view engine
   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     ViewEngines.Engines.Clear();
   5:     ViewEngines.Engines.Add(new WebFormViewEngine());
   6:     ViewEngines.Engines.Add(new RazorViewEngine());
   7:     ViewEngines.Engines.Add(new XsltViewEngine
   8:         {
   9:             ViewLocationFormats = new [] { "~/Views/{1}/{0}.xslt", "~/Views/Shared/{0}.xslt" },
  10:             PartialViewLocationFormats = new[] { "~/Views/{1}/{0}.xslt", "~/Views/Shared/{0}.xslt" }
  11:         });
  12:     RegisterGlobalFilters(GlobalFilters.Filters);
  13:     RegisterRoutes(RouteTable.Routes);
  14: }
now to use it just add you xslt file to your view folder like you would with any other view and return your serializable object to the the View you’re returning
   1: public ActionResult TransformedView()
   2: {
   3:     var model = new BasicDemoModel{Name = "Demo User"};
   4:     return View(model);
   5: }

for the full source (click here)

Saturday, March 24, 2012

Creating Custom Asp.net MVC ActionResults

One of the most powerful things in Asp.net MVC is how easy it is to extend, a really good example is ActionResults.  Basically all you have to do is inherit the ActionResult class and override the ExecuteResult method and add a constructor, with a few lines for code you can have an action return anything you want from dynamically generated pdf and xls files, to images. 

Here is an example of returning an object as xml
   1: public class XmlResult : ActionResult
   2: {
   3:     private readonly object _item;
   4:  
   5:     public XmlResult(object item)
   6:     {
   7:         _item = item;
   8:     }
   9:  
  10:     public override void ExecuteResult(ControllerContext context)
  11:     {
  12:         if (_item != null)
  13:         {
  14:             var xs = new XmlSerializer(_item.GetType());
  15:             context.HttpContext.Response.ContentType = "text/xml";
  16:             xs.Serialize(context.HttpContext.Response.Output, _item);
  17:         }
  18:     }
  19: }

basically you pass the object you want serialized into the constructor, and return it.  Inside the ExecuteResult method I do a quick null check, spin up a XmlSerializer set the response content type to “text/xml” and use the HttpContext.Response.Output to write the serialized data.

All you have to do to use it is create a new instance and return it from your action
   1: public ActionResult Index()
   2: {
   3:     var model = new BasicDemoModel();
   4:     return new XmlResult(model);
   5: }

To see the code used checkout the sample application at here.