Thursday, June 05, 2008

LINQ to CSV

I thought it would be nice to be able to produce a CSV file by doing something like this:

string ordersCsv = orderRepository.GetAll().Select(o => new 
{ 
    OrderId = o.OrderId,
    Email = o.Email,
    OrderStatus = o.OrderStatus.Name,
    CreatedDate = o.CreatedDate,
    Total = o.Basket.Total
}).AsCsv();

So here's an extension method to do just that:

public static string AsCsv<T>(this IEnumerable<T> items)
    where T : class
{
    var csvBuilder = new StringBuilder();
    var properties = typeof (T).GetProperties();
    foreach (T item in items)
    {
        string line = properties.Select(p => p.GetValue(item, null).ToCsvValue()).ToArray().Join(",");
        csvBuilder.AppendLine(line);
    }
    return csvBuilder.ToString();
}

private static string ToCsvValue<T>(this T item)
{
    if (item is string)
    {
        return "\"{0}\"".With(item.ToString().Replace("\"", "\\\""));
    }
    double dummy;
    if (double.TryParse(item.ToString(), out dummy))
    {
        return "{0}".With(item);
    }
    return "\"{0}\"".With(item);
}

It's work with anything that implements IEnumerable<T>, that includes the results of LINQ-to-SQL queries , arrays, List<T> and pretty much any kind of collection. Here's it's unit test:

[TestFixture]
public class EnumerableExtensionsTests
{
    [Test]
    public void GetCsv_ShouldRenderCorrectCsv()
    {
        IEnumerable<Thing> things = new List<Thing>()
            {
                new Thing
                    {
                        Id = 12,
                        Name = "Thing one",
                        Date = new DateTime(2008, 4, 20),
                        Child = new Child
                                    {
                                        Name = "Max"
                                    }
                    },
                new Thing
                    {
                        Id = 13,
                        Name = "Thing two",
                        Date = new DateTime(2008, 5, 20),
                        Child = new Child
                                    {
                                        Name = "Robbie"
                                    }
                    }
            };

        string csv = things.Select(t => new { Id = t.Id, Name = t.Name, Date = t.Date, Child = t.Child.Name }).AsCsv();

        Assert.That(csv, Is.EqualTo(expectedCsv));
    }

    const string expectedCsv = 
@"12,""Thing one"",""20/04/2008 00:00:00"",""Max""
13,""Thing two"",""20/05/2008 00:00:00"",""Robbie""
";

    public class Thing
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Date { get; set; }
        public Child Child { get; set; }
    }

    public class Child
    {
        public string Name { get; set; }
    }
}

MVC Framework: Capturing the output of a view (part 2)

Yesterday I wrote about a technique for capturing the HTML output by a view so that you could do something else with it, like send it in an email. However there turned out to be serious problems with that technique. Because it used the current HttpResponse instance it set the internal _headersWritten flag of the Response. When I subsequently wanted to execute a redirect I got an error saying:

"System.Web.HttpException: Cannot redirect after HTTP headers have been sent."

I tried to find some way to reset this flag, but there didn't seem to be any way of doing it. However, after digging around the MVC source code and with a bit of help from Reflector I found an entirely different (and better I think) approach. Rather than use the MVCContrib BlockRenderer I simply replace the current HttpContext with a new one that hosts a Response that writes to a StringWriter.

I've wrapped it all up in an extension method on Controller called CaptureActionHtml, so you can write code like this example from Suteki Shop that I use to send an order email confirmation:

[NonAction]
public virtual void EmailOrder(Order order)
{
    string subject = "{0}: your order".With(this.BaseControllerService.ShopName);

    string message = this.CaptureActionHtml(c => (ViewResult)c.Print(order.OrderId));

    var toAddresses = new[] { order.Email, BaseControllerService.EmailAddress };

    // send the message
    emailSender.Send(toAddresses, subject, message);
}

There are also overloads that allow you to call an action on a different controller than the current one, here's an example from a test controller I wrote where the TestController is executing the Item action on the orderController, using an alternate master page, called  "Print", and then writing the HTML output to a log:

public class TestController : Controller
{
    private readonly OrderController orderController;
    private readonly ILogger logger;

    public TestController(OrderController orderController, ILogger logger)
    {
        this.orderController = orderController;
        this.logger = logger;
    }

    public ActionResult Index()
    {
        string html = this.CaptureActionHtml(orderController, "Print", c => (ViewResult)c.Item(8));

        logger.Info(html);

        // do a redirect
        return RedirectToRoute(new {Controller = "Order", Action = "Item", Id = 8});
    }
}

The best way of seeing this all in action is to get the latest code for Suteki Shop, There's a stand-alone Suteki.Common assembly that you can include in your project that provides these extension methods in the Suteki.Common.Extensions namespace.

But here is the code for the extension class if you want to just cut and paste:

using System;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace Suteki.Common.Extensions
{
    public static class ControllerExtensions
    {
        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="masterPageName">The master page to use for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, masterPageName, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(targetController, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="masterPageName">The name of the master page for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            if (controller == null)
            {
                throw new ArgumentNullException("controller");
            }
            if (targetController == null)
            {
                throw new ArgumentNullException("targetController");
            }
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            // pass the current controller context to orderController
            var controllerContext = controller.ControllerContext;
            targetController.ControllerContext = controllerContext;

            // replace the current context with a new context that writes to a string writer
            var existingContext = System.Web.HttpContext.Current;
            var writer = new StringWriter();
            var response = new HttpResponse(writer);
            var context = new HttpContext(existingContext.Request, response) {User = existingContext.User};
            System.Web.HttpContext.Current = context;

            // execute the action
            var viewResult = action(targetController);

            // change the master page name
            if (masterPageName != null)
            {
                viewResult.MasterName = masterPageName;
            }

            // we have to set the controller route value to the name of the controller we want to execute
            // because the ViewLocator class uses this to find the correct view
            var oldController = controllerContext.RouteData.Values["controller"];
            controllerContext.RouteData.Values["controller"] = typeof(TController).Name.Replace("Controller", "");

            // execute the result
            viewResult.ExecuteResult(controllerContext);

            // restore the old route data
            controllerContext.RouteData.Values["controller"] = oldController;

            // restore the old context
            System.Web.HttpContext.Current = existingContext;

            return writer.ToString();
        }
    }
}

Wednesday, June 04, 2008

More Windsor Love: Using log4net integration

Adopting an IoC container has been a software revelation for me. It just keeps on getting better. I just added logging to Suteki Shop simply by doing the following steps (thanks to Sean Chambers for the details)

1. Add a reference to the following Castle Project dlls:

Castle.Facilities.Logging.dll
Castle.Services.Logging.Log4netIntegration.dll

2. Configure the LoggingFacility:

<facilities>
  <facility
    id="loggingfacility"
    configfile="log4net.config"
    loggingapi="log4net"
    type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging" />
</facilities>

3. Add the log4net config file to the root of the web application:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>

  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="sutekishop.log" />
    <appendToFile value="true" />
    <maximumFileSize value="100KB" />
    <maxSizeRollBackups value="2" />

    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%level %thread %logger - %message%newline" />
    </layout>
  </appender>

  <root>
    <level value="DEBUG" />
    <appender-ref ref="RollingFile" />
  </root>
</log4net>
</configuration>

And that's all. Isn't that cool, now I can simply add a dependcy for ILogger to any service in my application and log to my heart's content.

MVC Framework: Capturing the output of a view

Update: I found that this technique has problems because it changes the state of the HttpResponse by setting its internal _headersWritten flag which means you can't subsequently execute a redirect. I've developed a different and better technique which involves replacing the current HttpContext. See this post:

MVC Framework: Capturing the output of a view (part 2)

Sometimes it's useful to be able to capture the output of a view rather than simply have it streamed to the Response.OutputStream. A couple of things have made this quite easy to do. Firstly, from CTP 3 of the MVC Framework, controllers now return an ActionResult rather than just setting properties and leaving the infrastructure to act on them. This means we can take a ViewResult and call ExecuteResult on it before it's returned, allowing us to take control of the rendering.

The second innovation is a nice piece of work from the MVCContrib project. This is an open source add-on to the MVC Framework that provides lots of useful stuff like standard error handling, data binding, IoC container integration and lots more. If you're building MVC Framework applications you really should look at what this project can do for you. Deep in its bowels is the BlockRenderer class, this can take any action that renders to Response.OutputStream and divert it to a string. It does it by adding a custom filter to the Response filter chain.

Here's a silly example of it in action. I've created a test controller and in its Index action I'm calling the Item action on another controller called orderController. I'm then using the BlockRenderer to capture the rendering of the Item view in a string, which is then simply displayed.

using System.Web.Mvc;
using MvcContrib.UI;

namespace Suteki.Shop.Controllers
{
public class TestController : Controller
{
private OrderController orderController;

public TestController(OrderController orderController)
{
this.orderController = orderController;
}

public ActionResult Index()
{
// pass the current controller context to orderController
orderController.ControllerContext = ControllerContext;

// create a new BlockRenderer
var blockRenderer = new BlockRenderer(HttpContext);

// execute the Item action
var viewResult = (ViewResult) orderController.Item(1);

// change the master page name
viewResult.MasterName = "Print";

// we have to set the controller route value to the name of the controller we want to execute
// because the ViewLocator class uses this to find the view
this.RouteData.Values["controller"] = "Order";

string result = blockRenderer.Capture(() => viewResult.ExecuteResult(ControllerContext));

return Content(result);
}
}
}


Note that I change the name of the master page. You can imagine that the "Print" master page is a simple body tag container without any of the site furniture: menus and wotnot, that my standard master page might have. I also have to change the controller route value because the ViewLocator class of the MVC Framework uses it to find the view name.



The main reason I have for doing this, is so that I can send a view by email, but you can imagine how it might be useful to do other things like build up a page from multiple controllers and or views.

Developer Developer Developer Day 7

DDD7 (UK) has recently been announced. It's going to be held at the Microsoft campus in Reading on the 22nd of November. Submissions for talks have now opened. I've put forward two suggestions, both on IoC containers. The first will be an updated version of the IoC introductory talk I gave last year:

Why do I need an Inversion of Control Container

"Inversion of Control (IoC) containers such as Unity Windsor and Structure Map are a hot topic in the Microsoft Development world. What are they and why is there such a buzz about them? How will they help me build better applications? Join me on a trip to an parallel universe of application architecture where I show how IoC containers at last make component oriented software development a reality."

This is an introduction to IoC and IoC containers. It's a description of how the IoC design pattern is a simple way of building component oriented software and how IoC containers add a bit of magic to that party. The other talk I've suggested is a deeper look at the subject by examining its use in a real application:

Using an Inversion of Control Container in a real world application

"Going beyond an initial introduction to IoC containers, this talk shows their use in an open source eCommerce application, Suteki Shop. I will show how the IoC container helps us write component oriented software and can significantly simplify both our architecture and code. This will include a look at some nice techniques such as generic repositories, using IoC containers with the MVC Framework and how they can help us host services."

The idea here is to pass on my experience of building applications based around these techniques. There will be a lot of code and practical advice. I'm still on a steep learning curve myself with all this, so I expect that by November there will be a lot in this talk that I'm not even aware of yet.