Reporting with Web-Api in LightSwitch

Introduction

In a previous post, I introduced web-api as a simple commanding pattern in LightSwitch.

These command were basically doing “posts” over web-api. For a very specific type of commanding, i.e. the generation of reports, a slightly optimized approach can be applied.

How does reporting differ from generic commanding?

This will boil down to using a “GET” verb rather than a “POST”, and dispatching the download of the report to a separate html page. By doing so, the client stays completely “unblocked” when triggering a request for a report generation.

I’ll focus in this post not on the fancy aspects of reporting. When you know some of my previous post, I’m in the habit of focusing on “plumbing” rather than on “eye-catchers”. That’s me :)

You need the same nuget packages as in my previous post on Commanding with Web-Api.

What’s happening server side?

It’s a good practice to seperate the string report generation and the report controller. Separation of concerns, you know.

Let’s first have a look at the ReportEngine class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Microsoft.LightSwitch;
using System.Threading;
namespace LightSwitchApplication
{
     public static class ReportEngine
    {
        public static StringBuilder GetCustomerByLastNameReport(string lastNameSearchParameter)
        {
            StringBuilder stringBuilder = new StringBuilder();
            using (ServerApplicationContext ls = LightSwitchApplication.Application.CreateContext())
            {
                var query = ls.DataWorkspace.ApplicationData.Customers.Where(c => c.LastName.Contains(lastNameSearchParameter)); // can be any query !!!

                foreach (Customer customer in query)
                {
                    stringBuilder.AppendLine(customer.FirstName + ";" + customer.LastName);
                }
            }
            return stringBuilder;
        }
    }
}

Here again we make use of the preview 2 ServerApplicationContext to get access to the LightSwitch domain model. The above report has one simple parameter, allowing us to generate a report based on the lastname parameter. This can have any complexity you need.

The above ReportEngine will be called from the ReportController:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

namespace LightSwitchApplication
{
    public class ReportController : ApiController
    {
        private static readonly MediaTypeHeaderValue _mediaType = MediaTypeHeaderValue.Parse("text/csv");

        [HttpGet]
        public HttpResponseMessage CustomersByLastName(string lastName)
        {
            {
                System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
                MemoryStream memStream = new MemoryStream(encoding.GetBytes(ReportEngine.GetCustomerByLastNameReport(lastName).ToString()));
                HttpResponseMessage fullResponse = Request.CreateResponse(HttpStatusCode.OK);
                fullResponse.Content = new StreamContent(memStream);
                fullResponse.Content.Headers.ContentType = _mediaType;
                string fileName = String.Format("data-{0}.csv", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
                fullResponse.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("fileName") { FileName = fileName };
                return fullResponse;
            }
        }
    }
}

Simple indeed.

What do we need client side?

We have again a screen button that triggers following code:

partial void CustomersByLastNameViaWebApi_Execute()
        {
            string lastNameSearchParam = this.ShowInputBox("Give Last name", "Create Report");

            Dispatchers.Main.Invoke(() =>
            {
                Uri baseAddress = LightSwitchCommandProxy.GetBaseAddress();

                string url = baseAddress.AbsoluteUri +  @"api/Report/CustomersByLastName/?lastName=" + lastNameSearchParam;
                HtmlPage.Window.Navigate(new Uri(url), "_blank");
            });
        }

Conclusion.

So far I focused on a simple csv report, but I think (but I didn’t try it yet), the above approach would work for any other report type.

Moreover, your html Jquery-mobile client can re-use the same functionality !

Have fun with it.