ASP.NET MVC XSLT IViewEngine

June 1st, 2009

Here is my first attempt at creating a custom view engine for ASP.NET MVC, I was inspired by http://www.worldofwarcraft.com/ which uses the XSL/XML combination with client side transformations. I would assume this saves them a copious amount of bandwidth. I am surprised this hasn’t been done more often which leads me to believe there must be some flaws with this approach. But nevertheless here is a View Engine that provides the ability to render out XML/XSLT combos as well as do the transforms server side.

Currently I do not have a reliable way of detecting client side XSLT support but this could be done via interrogating the User Agent or even a XSLT test page that sets a cookie. The real win with this approach I can see is the user downloads your visual elements once (the XSL style sheet) and the data can change numerous times. I will post a more detailed tutorial on how to write your own View Engine at a later date.

        //Add this to your GlobalASAX
        protected void Application_Start()
        {
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new XsltViewEngine("~/Content/Views/"));
 
            RegisterRoutes(RouteTable.Routes);
        }
 
public class XsltViewEngine : VirtualPathProviderViewEngine
{
    public XsltViewEngine(string xsltHome)
    {
        ViewLocationFormats = new[] { xsltHome + "{1}/{0}.xslt", xsltHome + "Shared/{0}.xslt" };
        PartialViewLocationFormats = ViewLocationFormats;
    }
 
    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        return new XmlView();
    }
 
    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        if (ClientSideXsltSupported())
            return new XsltView() { ViewPath = viewPath };
        else
            return new XsltTransformView() { ViewPath = viewPath };
    }
 
    private bool ClientSideXsltSupported()
    {
        //TODO: work out a way to detect.
        //controllerContext.HttpContext.Request.Cookies["XsltSupported"]
        return true;
    }
}
 
public class XmlView : IView
{
    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    {
        var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType());
        var writerSettings = new XmlWriterSettings { OmitXmlDeclaration = true };
        using (var xmlWriter = XmlWriter.Create(writer, writerSettings))
        {
            if (xmlWriter != null)
                serializer.Serialize(xmlWriter, viewContext.ViewData.Model);
        }
    }
}
 
public class XsltView : IView
{
    public string ViewPath { get; set; }
 
    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    {
        viewContext.HttpContext.Response.ContentType = "text/xml";
        writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        writer.WriteLine("<?xml-stylesheet type=\"text/xsl\" href=\"" + VirtualPathUtility.ToAbsolute(ViewPath) + "\"?>");
        var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType());
        var writerSettings = new XmlWriterSettings { OmitXmlDeclaration = true };
        using (var xmlWriter = XmlWriter.Create(writer, writerSettings))
        {
            if (xmlWriter != null)
                serializer.Serialize(xmlWriter, viewContext.ViewData.Model);
        }
    }
}
 
public class XsltTransformView : IView
{
    public string ViewPath { get; set; }
 
    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    {
        var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType());
        using (var ms = new MemoryStream())
        {
            using (var xmlWriter = XmlWriter.Create(ms))
            {
                if (xmlWriter != null)
                    serializer.Serialize(xmlWriter, viewContext.ViewData.Model);
            }
            ms.Seek(0, SeekOrigin.Begin);
            var transformer = new XslCompiledTransform(true);
            transformer.Load(viewContext.HttpContext.Server.MapPath(ViewPath));
            transformer.Transform(XmlReader.Create(ms), null, writer);
        }
    }
}

System.Linq.Enumerable.Any - Better Know an Extension Method Part 3

February 20th, 2009

During this series I will be investigating the purpose and uses of the Extension methods made available in the System.Linq namespace specifically for classes that implement IEnumerable. If you have any gems you have created for the extension in question drop a comment below as I would love to see what people are coming up with. In Part 3 I will be exploring the very simple Any extension method described in the MSDN documentation as follows.

“Determines whether a sequence contains any elements.” - MSDN

When I first started looking at Any it seemed really strange at first, the first override just returns true if the Enumeration is greater than 0 which quite frankly is bizarre since its a direct copy of Count. It would just prove a neater way to write Linq expressions rather than the alternative using Count.

 
var orders = new List<Order>()
{
     new Order() {Total = 123.23},
     new Order() {Total = 512},
     new Order() {Total = 123.23},
     new Order() {Total = 5172.34},
     new Order() {Total = 647.518},
     new Order() {Total = 368.180},
     new Order() {Total = 12.84},
     new Order() {Total = 947.25},
};
 
if (orders.Any())
{
    //do something
}
 
if (orders.Count > 0)
{
    //do something
}

Both achieve the very same thing the second override is really useful it allows you to test all the items in the Enumeration for at least one match condition.

 
if (orders.Any(order => order.Total > 900))
{
    //do something
}

I am really struggling to come up with a good real world example of how/when to use Any so I wont at this time. But if you find yourself writing this code below, dont!

bool result = false;
foreach(var order in orders)
{
    if (order.Total > 900)
    { 
        result = true;
        break;
    }
}
 
if (result)
{
    //do something
}

Robot Overlords

February 19th, 2009
http://blogs.wsj.com/photojournal/2009/02/17/pictures-of-the-day-115/

http://blogs.wsj.com/photojournal/2009/02/17/pictures-of-the-day-115/

Reminds me of this painting in a spooky way. I for one welcome my new Robot overlords.

http://upload.wikimedia.org/wikipedia/commons/7/73/God2-Sistine_Chapel.png

http://upload.wikimedia.org/wikipedia/commons/7/73/God2-Sistine_Chapel.png