Search code examples
c#asp.net-mvccsvviewengineactionresult

Custom View Engine vs Custom Action Result


I have a scenario where the user have the option to click the button "download" and I should create a csv file, that contains history data, and then let the user save the file locally. As I haven't done this before, I started to look around for how to do this and basically came across both Custom View Engine, and Custom Action Result.

My question now is what is the benefit/disadvantages with these? What is the preferred way to go?

The CSV file, is basically just containing headers, and data (up to about 15 columns/fields) with a few thousand rows. so nothing special really.


Solution

  • I would probably go for a custom action result (the actual serialization in my example is done with FileHelpers):

    public class CsvResult<T> : ActionResult
    {
        public IEnumerable<T> Records { get; private set; }
        public CsvResult(IEnumerable<T> records)
        {
            Records = records;
        }
    
        public override void ExecuteResult(ControllerContext context)
        {
            var response = context.HttpContext.Response;
            response.ContentType = "text/csv";
            var engine = new FileHelperEngine(typeof(T));
            engine.WriteStream(response.Output, Records);
        }
    }
    
    [DelimitedRecord(",")] 
    public class Customer 
    {
        public int Id { get; set; }
        public string Name { get; set; }         
    }
    
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var customers = new[]
            {
                new Customer { Id = 1, Name = "customer 1" },
                new Customer { Id = 2, Name = "customer 2" },
            };
            return new CsvResult<Customer>(customers);
        }
    }
    

    You could even prettify this return statement (generics are ugly and superfluous in this case) by using an extension method:

    public static class ControllerExtensions
    {
        public static ActionResult Csv<T>(this Controller controller, IEnumerable<T> records)
        {
            return new CsvResult<T>(records);
        }
    }
    

    and then simply:

    public ActionResult Index()
    {
        var customers = new[]
        {
            new Customer { Id = 1, Name = "customer 1" },
            new Customer { Id = 2, Name = "customer 2" },
        };
        return this.Csv(customers);
    }