John Holmes and the case of the mysteriously missing LightSwitch record

Chapter one: the night of disappearance

The cold wind was howling in the streets when Holmes knocked on Elvis’ door.  “It just doesn’t make sense”, was the first thing Elvis said, “it’s simply… gone”…

Aight, pause please. I’m enjoying high 80s (Fahrenheit) at the moment here in the Caribbean, there was no door (just skype) and Elvis is not the actual name of the developer I was helping. I’ll try fiction again in a couple of years, let’s just get through the story of this missing LightSwitch record I encountered last week because I’m pretty sure one day, someone is going to stumble upon a similar issue and this blog post will save him/her half an hour of being dumbfounded…

Chapter one: (for real this time): a record went missing

So here’s what really happened: Elvis called me up because the LightSwitch desktop application that he was working on was throwing an unexplainable nullpointer exception. Unexplainable, because his flow was really basic, textbook easy even:

a) create a record from a “create new entity” screen (the entity was called a “case”)

b) when the screen has successfully saved the new record, use code to close the “create new entity” screen and open the default detail screen for that case enity

c) a nullpointer exception happens in the _initializeDataWorkspace method on this.Case

I’d never seen this before. Right after successfully saving, we show a detail screen on the same entity and the entity just disappears. I’m pretty sure at that point I spent at least 10 minutes of mumbling “I’m dumbfounded“. We started debugging and piece by piece we were able to reassemble the complete puzzle…

See, each screen in a LightSwitch desktop application has its own DataWorkspace.  This is LightSwitch’ implementation of the unit-of-work pattern, whatever changes happen in one screen get committed as a whole upon saving, and either all changes are persisted to the data source or, in the case of an exception, none.

Since each screen has it’s own DataWorkspace, a single entity can exist multiple times (multiple instances) in different DataWorkspaces.  This also means that, unlike the LS HTML client, when you type code like

this.Application.ShowDefaultScreen(this.Case);

the framework will actually take this.Case, an entity which belongs to the DataWorkspace of the “Create new entity” screen,  and pass only the ID field(s) to the new “Detail” screen.  The new screen will then actually use the ID(s) to retrieve the entity again, this time in its own DataWorkspace.

Chapter 2: the resource segment cannot be found

Sure enough, Fiddler soon revealed that when the second screen tried to retrieve the entity again, something went wrong.

Really wrong.

The exception returned was something along the lines of “the resource segment cannot be found for ‘http://localhost:xxxx/ApplicationData.svc/Case'”

Another ten minutes of mumbling about how dumbfounded I was, went by, before we got an Epiphany…

LightSwitch uses the OData protocol to interact with the server tier. OData uses http verbs (GET, POST, etc) for the ‘action’ (***), and a URI to state which entity you want to do your action on. URI is the keyword here: unique resource identifier. Or translated into normal language: your call to “blah blah blah.ApplicationData.svc/case/123” means you want me to retrieve the ‘Case’ entities and return the slice identified by “123”.

Get all resources for “Case” and only return slice (fragment) 123.

So basically this was the “OData way” of saying: case 123 can’t be found.

Thanks for being straightforward and all…

But… we just created it. And according to our select * from Case, the record is in the database, then why could LightSwitch not find it?

Chapter 3: if it looks like a duck

The mystery continued with 10 more minutes of mumbling about dumbfoundedness… But then we remembered that there’s actually a filter active.

LightSwitch supports row-level filtering, ideal for slicing your records in multi-tenant or advanced security/permission situations.

And sure enough, this was the case

if(!Application.Current.User.HasPermission(Permissions.FamilyLaw)
  query = query.Where( case => case.AssignedCategory.Name != "Family");

The user we were testing with did not have the FamilyLaw permission any more, effectively causing all cases to be filtered to only show the ones where the assigned category is not “Family”.

Right?

Right?

Turns out, our problem was even a little more subtle…

This LINQ query filters out all the cases that have an AssignedCategory that is not “Family”.

Read that again… The linq query is actually, unintentionally I assure you, adding two filter criteria behind the scenes:

  case.AssignedCategory != "Family"
  seems to be interpreted as...
  case.AssignedCategory != null && case.AssignedCategory.Name != "Family"

Really, LINQ? Really?

Once we rewrote the LINQ query (to allow cases with AssignedCategory == null), our freshly created case entity (which has not yet been assigned to any category) now found its way down to the details screen.

 

*** PS: LightSwitch doesn’t really use HTTP verbs like (PUT, PATCH, …) directly on the OData service.  Because each screen has it’s own “unit of work”, all changes get posted just once, together, to an URI called $batch.  This HTTP “batch” call actually contains all different changes that happened in a screen (delete this, modify that, etc) in a single HTTP call.  Using transactional techniques, all changes then all get persisted in the data source together or, in the case something goes wrong, none of them get persisted.

Preventing a screen from closing in the LightSwitch desktop client

Every day, one of our end-users opens a screen and prints out all open support tickets for that day. When the tickets are printed, we want to make sure those tickets are marked as ‘printed’ on the server, so we have a button on the screen that lets her do just that:

        //Screen has one 'state' boolean
        private bool isMarkedAsPrinted = false; 

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 

Sometimes, she’ll forget to press that button, so when the screen closes we want to check if the tickets are printed.  The screen has a ‘Write Code’ button for the _Closing event, and it is passed a bool called ‘cancel’. If you set this bool to ‘true’, the screen closing event will be cancelled.

        //Excecuted when the screen is closed
        partial void PrintTicketScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'reallyClose' flag 
            cancel = !(isMarkedAsPrinted); 
        }

That works like a charm. Once she prints, the isMarkedAsPrinted boolean is set to true, and the screen can close. When she forgets to press the button, the screen will not close no matter how many times the ‘x’ is clicked.

From a UX perspective though, this is rather weird. The application will just feel like it doesn’t respond to her trying to close the screen. We could have the screen show a message box saying she needs to print first, or better yet: asking her if she wants to print:

        //Screen has one 'state' boolean
        private bool isMarkedAsPrinted = false; 

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 
         
        //Excecuted when the screen is closed
        partial void PrintTicketScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'isMarkedAsPrinted' flag 
            cancel = !(isMarkedAsPrinted);

            if (cancel)
            {
                var answer = this.ShowMessageBox("Would you like to mark these tickets as printed before closing the screen?", "Mark tickets as printed?", MessageBoxOption.YesNoCancel);
            switch (answer)
            {
                case MessageBoxResult.Cancel:
                    //Simply let the screen stay open in current state
                    break;
                case MessageBoxResult.Yes:
                    //Execute mark as printed, then close screen automatically
                    this.MarkAsPrinted_Execute();
                    cancel = false;
                    break;
                case MessageBoxResult.No:
                    //User doesn't want to mark as printed, close screen
                    cancel = false;
                    break;
                default:
                    throw new Exception("Unexpected result.");
            }
            }
        }

Something weird will happen when you run through the code though… When closing the screen, if the tickets haven’t been marked as printed, the dialog will be displayed to remind the user to save. However, half a second later, the screen will close anyways.

Here’s the rub: your screen’s _Closing event is given about one or two seconds to finish executing. When it does not finish executing within that timeframe, the LightSwitch framework will close every open dialog on the screen (including your ‘would you like to mark the tickets as printed’ one), and close the screen anyway.

The workaround is to make sure the _Closing event code returns immediately, but cancels the closing, on first run. Before we return from that method though, we’ll kick off a background worker. This background worker will ask the screen’s logical dispatcher to show the ‘would you like to mark the tickets as printed’ dialog. This request will be queued by the screen’s logical dispatcher and executed whenever it has time (read: whenever it is done not closing your screen).

Once the tickets are marked as printed, or the user refuses to mark the tickets as printed (perhaps the screen was opened by accident in the first place, or the printer ran out of paper), we’ll flip a boolean and close the screen programmatically. The second time the screen’s _Closing event code is executed, it’ll pick up this boolean (or notice the tickets have been marked as paid) and let the screen close this time:

        //Screen has two 'state' booleans
        private bool isMarkedAsPrinted = false;
        private bool userDoesNotWantToMarkAsPrinted = false;

        //Normal code behind a button to mark tickets as printed
        partial void MarkAsPrinted_Execute()
        {
            if (!isMarkedAsPrinted)
            {
                foreach (var ticket in this.Tickets)
                    ticket.IsPrinted = true;
                this.Save();
                this.isMarkedAsPrinted = true; //Don't forget to change the screen's state
            }
            else
                this.ShowMessageBox("The tickets are already marked as printed.");
        } 
         
        //Excecuted when the screen is closed
        partial void PrintTicketsScreen_Closing(ref bool cancel)
        {
            //Cancel all closing events unless we set the 'isMarkedAsPrinted' flag OR 'userDoesNotWantToMarkAsPrinted'
            cancel = !(isMarkedAsPrinted || userDoesNotWantToMarkAsPrinted);

            if (cancel)
            {
                //If this method takes longer than a second, the LS framework will close all dialogs as 'cancelled'
                //Hence, we start a backgroundWorker so that we can return from this method straight away
                var sleepy = new BackgroundWorker();
                sleepy.DoWork += (s, e) =>
                {
                    //This code will execute on the thread of the background worker, so
                    //we must ask the screen's dispatcher to invoke the 'askPrintBeforeClosing' 
                    //instead of asking this on the background worker's thread
                    this.Details.Dispatcher.BeginInvoke(() => { askPrintBeforeClosing(); });
                };
                sleepy.RunWorkerAsync();
            }
        }

        //Helper method
        void askPrintBeforeClosing()
        {
            var answer = this.ShowMessageBox("Would you like to mark these tickets as printed before closing the screen?", "Mark tickets as printed?", MessageBoxOption.YesNoCancel);
            switch (answer)
            {
                case MessageBoxResult.Cancel:
                    //Simply let the screen stay open in current state
                    break;
                case MessageBoxResult.Yes:
                    //Execute mark as printed, then close screen automatically
                    this.MarkAsPrinted_Execute();
                    this.Close(false);
                    break;
                case MessageBoxResult.No:
                    //User doesn't want to mark as printed, close screen automatically
                    userDoesNotWantToMarkAsPrinted = true;
                    this.Close(false);
                    break;
                default:
                    throw new Exception("Unexpected result.");
            }
        }

Keep rocking LS!
Jan

 

 

 

 

 

New Syncfusion eBook: #LightSwitch mobile business apps Succinctly

Proud to present: Syncfusion’s newest addition to their Succinctly series: LightSwitch mobile bussines apps, written by yours truly.

Image 096

 

The first eBook I wrote for Syncfusion, LightSwitch succinctly, was a small introduction to LightSwitch’s designers, server, and desktop client (Silverlight).  I got some great feedback so when Syncfusion asked me (some time last year) to write a follow up, I was honored and anxious to get started. I was even more honored when I found out my technical reviewer was community champion Alessandro Del Sole!  (Thanks man!!)

This book picks up where the first one left off, describing some newer features like the DB projects and (of course) the HTML client.  I did some pretty advanced looking UI customizations, and was surprised myself in how easy the HTML client actually is to customize…

I hope you enjoy this one as much as I did writing it!

Keep rocking LS!

Jan

 

Why your hyperlink won’t work in the LS HTML client, part 2

Apparently, there is a part2 in my struggle with this ‘current user hyperlink’ that I inserted in my LightSwitch HTML client as a custom ‘control’

Image 068

At first sight, the link works perfectly: when you click it, the app shows a popup where you can edit your profile.  Because of the databinding, the hyperlink will automatically update its text when the user edits his/her name in that dialog.

At first sight…

Once I added multiple screens to my application, I noticed that the hyperlink databinding behaves correctly when I first open the screen, but after navigating to another screen and then back again, my custom control stopped working.  In fact, after navigating to another screen and then back again, half of my custom controls were behaving funny, the other half was just fine.  And by the time I noticed this subtlety, I had way too much custom controls on my screen to log it as a ‘known issue’ 😉

Image 069

I don’t remember ever running into problems with this before, but apparently there is a subtle difference in behavior between two different ways I interact with my custom ‘controls’.  Zooming in on the ‘offending’ code reveals the problem (when you know where to look, bugs are always easier to find):


var userViewer = $('<a id="userviewer" href="#" target="_self" >' + user.Name + '</a>');
userViewer.appendTo($(".msls-screen-buttons"));
//interaction 1
userViewer.on('click', function () {
myapp.showEditMyProfile(user);
});
contentItem.dataBind("value.Name", function () {
//interaction 2
$("#userviewer").text(contentItem.value.Name);
});

view raw

offendingJs.js

hosted with ❤ by GitHub

I create the usercontrol via a JQuery selector, then save a ‘reference to the usercontrol’ in a variable.  In my first interaction, I use this variable directly.  For some reason, in my second interaction, I use a new JQuery selector to get a new ‘reference to the same usercontrol’.

This seems to work fine for the very first time, but breaks silently on subsequent rendering.

The first fix seems to be the obvious: use the variable instead of using a new JQuery selector.

A second fix that seems to work, is to start the JQuery selector using the parent element (passed as an argument to your _render or _postRender functions) as the context:

$("#userviewer", element);

If anyone can enlighten me as the technical reasoning behind this issue, I’d love to hear it 🙂  Until I understand, I think I’ll stick with this last approach because it seems to be the most stable one…

 

Keep rocking LS!
Jan

 

 

Why your hyperlink won’t work the LS HTML client

(Update: read part 2 as well if you intend to use this code)

In our quest for happiness, my wife and I decided to live in the Caribbean for a couple of years.  Sipping cocktails on white beaches, watching Mojo (my goofy boxer dog) play in the clear blue water, while prototyping a LS app on my laptop.  I can tell you, life doesn’t get more stress free than this.

Unless… you need to add a hyperlink to that LS app…

In the top right corner of my app, I wanted to replace the Logout button with information about the currently logged in ‘user’.  For this app, I have a table with ‘Realtors’, one record per ‘user’ (login+pw combo).  On my screen, I added a local ‘Realtor’ property named ‘Myself’.  I dragged that property on my screen’s visual tree, selected ‘Custom Control’, and clicked the ‘Edit Render Code’ from Properties Window.

A JS file is generated containing a function stub to work my magic.  So far so good.  I bashed together some code to hide the Logout button and insert my link instead.

F5 this!

Image 061

Success!  Now let’s just give that link a quick click to verify it opens the EditMyProfile page…

Wait, whot?

Adding a hyperlink in an HTML5 app might sound like the most trivial assignment ever, but no matter what I tried, the link was not responding to click events.

Hours later (actually 1 and a half Mojitos, so probably not even close to ‘hours’), I found that my custom control wasn’t working because LS was purposely rendering a div named ‘msls-state-overlay’.  Apparently, when you create a custom control, LS by default will take care of the ‘Disabled Rendering’ for you.

And apparently, my link was determined to be rendered as disabled.

And apparently, like all great things in LS, if you don’t like the default behavior, change is but a setting away…

Image 062

One F5 and two sips of Margarita later…

Image 063

Works like a charm… At first sight…  There’s a subtle issue with the code, perhaps I had too much Margaritas :-s

Keep rocking LS!

 

Jan

 

Quick tip: LS Excel importer speed improvements

The LightSwitch desktop client has a great extension that lets you load an excel file, map the columns to your LightSwitch entities, then import the data in your application.

The source code is available and provides a great start to build an excel importer that fits your needs specifically.  Besides a couple of functional shortcomings (find the line ‘//TODO multiple worksheets’), it has one really big technical flaw: it uses COM interop to read the excel file.

This means that it’s really slow (3 mins for a 5k line file), the end-user MUST have excel installed on the local pc AND it works for Out-Of-Browser apps only.

However, an excel file is really just a zip that contains a couple of XML files.  If you replace the COM interop with some code that unzips the file and interprets those XML files instead, it becomes really fast (less than a second for a 5k line file), the end-user does not need to have excel installed on the local pc AND it works for in-browser apps too!

SilverlightShow.Com has a really great post on how to read and excel file via XML parsing.  It returns the data as an IEnumerable of Dictionary of ColumnName -> CellValue though, so you’ll need to mold it a bit:

                        FileInfo f = dialog.File;
                        XLSXReader reader = new XLSXReader(f);                        
                        var data = reader.GetData(reader.GetListSubItems().First()).ToArray();

                        var columns = data[0].Keys.OfType().ToList();
                        object[,] values = new object[data.Length, columns.Count];

                        for (int r = 0; r < data.Length; r++)
                        {
                            var row = data[r];
                            foreach (var c in row.Keys)
                            {
                                values[r, columns.IndexOf(c)] = row[c];
                            }
                        }

                        _excelDocRange = values;

Keep rocking LS!

Jan

 

PS:sorry my content is reaching you in ‘waves’, but I have so many pots cooking at the same time I often do not find the time to serve you a decent lunch…  😉

Do you serve your Azure Web Site hot or cold (Updated)?

9 nights after my previous inscription, my little birds in the realm have whispered me tales of this new Azure Web Sites feature called “Always On support“.  Thank you for bringing this to my attention, Jürgen, you shall be rewarded 2 extra virgins.

Apparently, them cloud people have a new weapon to protect against winter turning your web sites cold…

image_1A5E2489

Keep in mind that your coins shall be taken by the hour that your Web Site are hot, so some of us might stick to the iron price of an Azure Scheduled Job.

Keep rocking LS!

Jan

Do you serve your Azure Web Site hot or cold?

Internet tweeps seem to be in a debate about where the coldest spot right now would be: Fargo (ND), Mars, Winnipeg (Canada) or Yakutsk (Russia) (in that order, apparently). Honestly, that’s one race I’ll gladly pass on.  I hate cold, I’ll take hot over cold any day.  The weather, my coffee, my wife, my Azure Web Sites, …

Wait, never heard of cold or hot Azure Web Sites?

Azure Web Sites are running in a shared pool of resources.  To optimize this resource pool, the smart engineers at MS have decided that if one site in that pool does not have any user activity for roughly 20 minutes, resources are no longer allocated for that site.  The IIS process for that site gets killed, and the site is now ‘cold’.  After going cold, when a user connects to the site, Azure reloads your site and the site is now ‘warm’ until the next span of 20 minutes without user activity. As part of resource optimization, this 20 minute span may even be shortened if the other web sites in your web site’s resource pool are very busy (although it is believed never to go below 5 minutes).

I love Azure Web Sites and they are by far the most hassle-free way to deploy a LightSwitch application.  However, I do have a couple of problems with serving up cold web sites to my customers:

  • The first time a user connects after inactivity, he/she has the perceived user experience that the web site is slow, or needs to ‘wake up’.  This usually happens twice a day (mornings and after lunch).  If your web site is rather large (which happens quickly with LightSwitch apps), the call might even time out before Azure has your server process fully warmed up.  Two timeouts per day, for LOB apps with a small amount of users especially, does not boost customer confidence.
  • Even worse, if your web site happened to be a LightSwitch Desktop app (Silverlight OOB), that first call happens to be the SL app checking if the currently installed version still matches the server.  If that call times out, the app displays a big red warning sign stating that the application could not check for updates and could not be started.  Ouch!
  • Sometimes you might want to start a background thread to do a limited amount of simple event processing (not the most reliable or scalable architecture, but by far the cheapest).  When you do, you do not want to depend on user activity to keep this process from going cold.

Thankfully, if you decide there’s a good reason why your web site should not go cold, there are a number of good solutions to keep the oven burning.  One is the built-in feature called ‘Always On‘ (added after this article originally was published), this article will demonstrate the same effect using Mobile Services’ Job Scheduler.

An Azure scheduled job consist out of two parts: how often do you want to run, and what do you want to run.

Schedule your job to run every 5 minutes.  Image 279

I’ve done some test and these ‘5 minutes’ do not have a millisecond precision, but close enough.  Besides: 5 minutes is the absolute shortest amount of time before your site goes cold if all other web sites in your pool are really busy, so we should be covered.

Then, time to paste some JavaScript in the ‘script’ tab.  The only thing the script needs to do, really, is generate some ‘user activity’ in our Azure Web Site.

require('request').get({ url: "https://mysite.azurewebsites.net/HTMLClient/default.htm" });

Keeping Azure Web Sites alive means providing ‘incoming user activity’, like any HTTP GET to a web page or Web API, etc…  You can use a single script to keep as many sites alive as you want.  Azure should give you one free job, but it has a limitation of 16667 API calls and 165 outgoing MB per day.  This means that at a pace of 1 call per site per 5 mins, you can keep 57 sites warm without spending a single penny.

In a final note, a couple of days after this writing article, an update was published adding an Azure feature called ‘Always on’.  This also means ‘always billed’, and if you’re really cheap ( like me) and have more than 57 sites to keep warm (also… like me) then you could consider skipping some hours.  When you do, though, don’t forget that all Azure Servers, no matter where they are deployed, always run on UTC time…


function CheckMySite() {
//Thanks to Sandrino Di Mattia: http://fabriccontroller.net/blog/posts/job-scheduling-in-windows-azure/
warmUpSite("https://mysite.azurewebsites.net/HTMLClient/default.htm&quot;);
warmUpSite("https://mysecondsite.azurewebsites.net/HTMLClient/default.htm&quot;);
}
function warmUpSite(url) {
var currentdate = new Date();
//Poor man's time zone conversion to Central Time
currentdate.setHours(currentdate.getHours() 6);
//Three cheers for JS date formatting
var datetime = "" + (currentdate.getMonth() +1) + "/"
+ currentdate.getDate() + "/"
+ currentdate.getFullYear() + " @ "
+ currentdate.getHours() + ":"
+ currentdate.getMinutes() + ":"
+ currentdate.getSeconds();
if( currentdate.getHours() >= 7 || currentdate.getHours() < 19)
{
var req = require('request');
req.get({ url: url }, function(error, response, body) {
if (!error) {
console.info(datetime + " Ok");
} else {
console.error(datetime + 'error warming up ' + url + ': ' + error);
}
});
}
else
{
console.info("Not within office hours " + currentdate.getHours());
}
}

view raw

script.js

hosted with ❤ by GitHub

Keep rocking LS!

Jan

#LightSwitch and #Syncfusion: simply beautiful applications ($10.000 April raffle!)

With all the blog posts about the new LightSwitch HTML client, it’s easy to lose track of the continued investments in the Silverlight client and the server tier…

One minute summary

Today, this blog post will teach you what you can create by combining 30 minutes of your time with your existing LightSwitch skills, a little ASP.Net Web API creation, some Nuget-for-beginners and custom Silverlight controls to add a dashboard to a LightSwitch application.

Or, because a picture says a thousand words: today we’re going to create this (click-to-enlarge):

LightSwitch & syncfusion: deliver simply beautiful applications

Or, because a sample says more than a thousand pictures: <Download link coming soon> .

Ten minute summary

Prerequisites

To follow this guide, you’ll need:

1. Creating reports on the server

Instead of dragging all records over the wire, we’re going to access the ApplicationServerContext directly on the server, aggregate and transform the data there, then send the summary back.

You’ll know why the latter approach is preferred once you pay for your own Azure bills. :p

Hit the “Toggle View” button in solution explorer so that the LightSwitch application is in File View.  Then, right-click on the server project > Add a folder > Name:”Controllers”.

Next, right-click that new folder and choose to Add New Item… > Web API Controller Class > Name:TopRegionsController.cs  (If you are unfamiliar with ASP.Net Web API, you can skim through this tutorial.)

Before actually implementing the controller, we’ll configure the server for these new controllers.  Right-click the project > Add New Item > Global Application Class:

using System;
using System.Web.Http;
using System.Web.Routing;

namespace LightSwitchApplication
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {

            RouteTable.Routes.MapHttpRoute(
               name: "ReportsApi",
               routeTemplate: "reports/{controller}"
               );

        }
    }
}

Back to the controller, where we’ll implement the GET command:

using System;
using System.Linq;
using System.Web.Http;
using Microsoft.LightSwitch; //1.

namespace LightSwitchApplication
{
    public class TopRegionsController : ApiController
    {
        // GET api/
        public object Get()
        {
            using (ServerApplicationContext context = ServerApplicationContext.CreateContext()) //2.
            {
                var query = context.DataWorkspace.NorthwindData
                    .Orders
                    .Where(o => o.ShipRegion != null)
                    .GroupBy(
                        o => o.ShipRegion) //3.
                    .Select(
                        g => new //4.
                        {
                            Label = g.Key,
                            Value = g.Count()
                        }
                        )
                        .Execute() //5.
                        .OrderByDescending(g => g.Value)
                        .Take(15)
                        .ToList();
                return query;
            }
        }
    }
}

Some code highlights:

  1. Don’t forget to add the using statement to bring the Microsoft.LightSwitch namespace into scope.
  2. This is the powerfull ServerApplicationContext.  An IDisposable  , type-safe object that represents your LightSwitch application on the middle tier.  You can access user information, the Dataworkspace, …
  3. Pro tip! This GroupBy method (only available after step 1.) is something you can’t do over OData.  The grouping itself is done directly in the data source, if your data source supports it.  SQL obviously does.  (In other words, calling this GroupBy method will cause your SQL query to contain a GROUP BY statement :-))
  4. We’re gathering the results in an anonymous type.  Bluntly put: why bother?
  5. Don’t forget to execute the query!

Et voila, the server is done.  One quick way to test if you have done everything correctly, is to press F5 to start debugging, then navigate your browser or fiddler to http://localhost:<assigned port number>/reports/TopRegions .

Image 037

2. Fetching the data from the client

Add an empty Screen to the LightSwitch (Silverlight: in or out-of-browser) application called “DashBoards”, using the “List and Details” screen template but leaving Screen Data blank.

From the Screen Designer, hit the “Write Code” button and choose “DashBoards_Activated”.  This will generate a method stub for you, where you can add some code to match the following outline snippet:

using Microsoft.LightSwitch.Presentation.Extensions;
using System.ComponentModel;
namespace LightSwitchApplication
{
    public partial class Dashboards : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public object TopRegions { get; set; }

        partial void Dashboards_Activated()
        {
            //What comes here? :-s
        }
    }
}

As you can see, we’ve added a local property called “TopRegions”, of type object.  Because this can’t be done from the screen designer, we’ll have to notify when the property changes ourselves too.   The big question is what we should fill in as method body.  How do we find out what the server address is, perform the HTTP GET call asynchronously, and handle exceptions?

Luckily, when there’s a tough technical challenge, you can often hire “a developer in a box” (as I call it), or “Paul Van Bladel in a Nuget package” (as normal people would refer to it).

Open the package-manager console (if you don’t know what that is: View>Other Windows>Package Manager Console), make sure the LightSwitchApplication.Client project is selected as Default Project, then type:

Image 038

Pro tip!: after typing “inst” or “thelight”, you can press tab and have the rest of the command auto-completed.

This package will install some utility extension methods, which we can call from our method body (and effectively reduce this article from 14 pages to just 4):

        partial void Dashboards_Activated()
        {
            this.StartWebApiRequest<MySyncfusionControls.DataItemViewModel[]>//1
                ("reports/TopRegions",//2.
                (exception, result) =>
                {
                    if (exception != null)
                    {
                        this.ShowMessageBox("There was an error while fetching the data.");

                    }
                    else
                    {
                        Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => //3.
                            {
                                TopRegions = result;
                                this.PropertyChanged(this, new PropertyChangedEventArgs("TopRegions"));  
                            }
                        );
                    }
                }
            );
        }

Some code highlights:

  1. StartWebApiRequest is one of the methods added by the Nuget package, doing all the heavy lifting.  Thanks Paul!  We call the method and (by using the generic method signature) ask to cast the return value (a JSON list of anonymously typed stuff) to an array of MySyncfusionControls.DataViewModel (We’ll create this class in a second, by the way).  In a loosely typed language like JavaScript, or in an un-sandboxed environment like WPF, you could get away with not casting the return value (ie: casting it to object) but you’ll run into security problems trying to bind to it from the user controls later.  (Silverlight is very harsh on reflection-lovers, meaning safer for the end-user).
  2. This is the last part of the URI by which the server is exposing it’s endpoint, determined by the routing we set up in the Global.asax file, and the name of the controller.
  3. We’re currently on the logical thread.  This event will be handled on the UI thread.  Read about the incredible dual dispatcher objects in LightSwitch… O… wait…  //TODO: can someone blog about dual dispatcher objects?

 3. Creating the user control

To visualize the report to the user, you need some kick-ass controls.  LightSwitch Controls come in three flavors:

We’ll take the latter route: right-click on the solution > Add New Project > Silverlight 5 Class Library > Name:MySyncfusionControls

Replace the default class1 with:

    public class DataItemViewModel
    {
        public string Label { get; set; }
        public double Value { get; set; }
    }

This is the DTO we’ll use, duly note that the names of the two properties (“Label” and “Value”) match the one used in our anonymous type used to return the data from the server.

Goody, now for the actual control.  Add references to Syncfusion.Chart.Silverlight, then right-click the project > Add New Item > Silverlight User Control > Name:ColumnChartWrapper.  Replace the XAML with:

<UserControl x:Class="MySyncfusionControls.ColumnChartWrapper"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:syncfusion="clr-namespace:Syncfusion.Windows.Chart;assembly=Syncfusion.Chart.Silverlight"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <syncfusion:Chart Margin="10" Name="Chart1" MinHeight="300" MaxHeight="450" MinWidth="600" MaxWidth="1000" Background="White">
            <syncfusion:ChartArea BorderThickness="0">
                <syncfusion:ChartArea.Header>
                    <TextBlock FontSize="20" Text="{Binding DisplayName}" />
                    <!-- 1. -->
                </syncfusion:ChartArea.Header>
                <syncfusion:ChartArea.PrimaryAxis>
                    <syncfusion:ChartAxis RangePadding="Normal" ValueType="String" Interval="1"/>
                </syncfusion:ChartArea.PrimaryAxis>
                <syncfusion:ChartArea.SecondaryAxis>
                    <syncfusion:ChartAxis RangePadding="Normal" IsAutoSetRange="True"/>
                </syncfusion:ChartArea.SecondaryAxis>
                <syncfusion:ChartSeries x:Name="series" 
                    BindingPathX="Label" 
                    BindingPathsY="Value" 
                    DataSource="{Binding Value.TopRegions}" 

                    Type="Column" StrokeThickness="0"
                    ColorEach="True"
                    EnableEffects="True"
                    EnableAnimation="True" AnimateOption="Rotate" AnimateOneByOne="True" AnimationDuration="00:00:10"
                    >
                    <!-- 2. -->
                    <syncfusion:ChartSeries.AdornmentsInfo>
                        <syncfusion:ChartAdornmentInfo x:Name="Adornments_pot" Visible="True" SegmentLabelFormat="0.0"
                                                                   AdornmentsPosition="Top" 
                                                                   HorizontalAlignment="Center" 
                                                                   VerticalAlignment="Bottom" >
                            <syncfusion:ChartAdornmentInfo.LabelTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <TextBlock  TextWrapping="Wrap" Text="{Binding}" FontSize="20" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                        </TextBlock>
                                    </Grid>
                                </DataTemplate>
                            </syncfusion:ChartAdornmentInfo.LabelTemplate>
                        </syncfusion:ChartAdornmentInfo>
                    </syncfusion:ChartSeries.AdornmentsInfo>
                </syncfusion:ChartSeries>
            </syncfusion:ChartArea>
        </syncfusion:Chart>
    </Grid>
</UserControl>

Some XAML highlights:

  1. Bind to the DisplayName (which we’ll set in a minute)
  2. Well…
    • DataSource is set to Value.TopRegions.  The datacontext is the ContentItem that we’re about to drag on the screen designer.  This ContentItem has a property “Value”, which we’ll bind to the “Screen” in the screen designer.  This screen has a property called “TopRegions”, which we added in the code earlier.
    • BindingPathX and BindingPathsY are set to “Label” and “Value”, which are the names of the properties on or DTO that we created earlier.  This is where you would run into troubles if you didn’t cast the JSON result to a strongly typed object in a strongly typed language in a sandboxed environment.
    • Animations: you need lots of them and Syncfusion has some truly beautiful animations.  For demos, always include “EnableAnimation=”True” AnimateOption=”Rotate” AnimateOneByOne=”True” AnimationDuration=”00:00:10″”, you only get one shot at making a first impression so make sure there’s lots of shinies to catch your audience’s attention .  Pro tip! For production code, removing animations makes customers happier.

4. Finishing the screen

The heavy lifting is over…  So far, we’ve used ASP.Net Web Api and the LightSwitch ApplicationServerContext to gather and expose custom data reports, we’ve used C# code and a Nuget package to get that data to the client, and we created a custom Silverlight control based on one of the 7 billion (more or less) Syncfusion charting controls to visualize that data.

The glue to bind this all together: open the Dashboards screen in the screen designer, and in the middle area, select the main root (labeled “Columns Layout – Dashboard”) > Click on the Add link > New Custom Control. > Add Reference to the MySyncfusionControls library that we created, then > Select ColumnChartWrapper  > Data: Screen (ie: leave the default) > hit OK.

Image 024

Before finishing, select the user control in the screen designer, then in Properties Panel:

  • Set Display Name to “Top Regions”.  Remember the first of the XAML highlights? We’ll bind to this Display Name from the control. 🙂
  • Set Label Position to “None”
  • Set Horizontal Alignment to “Stretch”
  • Set Vertical Alignment to “Stretch”

Also, select the Save button in the screen command bar and in Properties Panel, set Is Visible to false.

Finally, press F5…

Boom!

Image 026

By the way: the purple column seems to be falling, that’s because I intentionally took the screenshot in the middle of the  “Rotate” loading animation.  – Showoff…

Thirty minute summary

There is no thirty minute summary, so rinse, repeat, try out some other charting controls, …  I added two more custom controls in the sample <Download link coming soon>, (don’t forget the prerequisites) which you could already see in action at the top of this blog post!  

If you like what you saw, feel free to leave a comment and automatically enter the Syncfusion Raffle…

O yes…  “Almost forgot”… (wink, wink, nudge, nudge) <Smooth transition> 

The Syncfusion Raffle!

You might think that this blog post is a little biased towards Syncfusion.  I won’t deny that, but in fact, the opposite is also true: Syncfusion is biased towards my blog readers.  Even more so, they are giving away five Enterprise Syncfusion Essential Studio licences worth $1999 each!

Let that sink in a little…  Syncfusion is giving my blog readers TEN THOUSAND DOLLARS of licences!

To enter the raffle:

  1. Before May 1st 2013: send out a tweet containing the #LightSwitch and #Syncfusion hashtag, and a link to this blog post.  For example: “#LightSwitch and #Syncfusion: simply beautiful applications. http://wp.me/p1J0PO-eW ” or “Thousands of dollars worth of #Syncfusion licences thanks to this #LightSwitch blog http://wp.me/p1J0PO-eW” or …
  2. Grab the permalink, and post the link to your tweet as a comment to this blog post (make sure you use a valid email address to comment, I need to be able to contact you if you win!)

One entry per person, normal contest rules apply.  If you don’t have twitter, just post the message on your blog, facebook, … 

The winners will be announced on May 1st 2013.

Best of luck, and don’t forget:

Keep rocking LS!

PS: not fan of all those colors?  Don’t worry, the sample <download link coming soon> shows you how add a combo box so you can change your color schema at runtime from “boring grayscale” to “metro” to “acid tripping”… 🙂

Image 039

LightSwitch news RSS: bringing you news from around the world! (http://bit.ly/lightswitchnewsRSS)

So…. What’s up doc?

Not much.  Reading some blog posts by other LightSwitch community members…

Sweet! Google Reader?

Errr… Haven’t you heared? Google is dead.  (Google reader*)

The easiest way to keep up to date with the other LightSwitch community members is to wait until the end of the month, then read Beth Massi’s Community & Content Rollup.  Unfortunately, there’s two problems with that approach:

Don’t you have your own twitter bot to keep you up to date?

Well yes, to keep myself up to date on a daily bases (I like news as hot as it gets), I turned each interesting “source” into a tweet by my twitter bot using ifttt.com

Unfortunately, this bot had some weird hiccups lately so after an extremely exhausting week I decided to spend a Friday afternoon rethinking my news model…

Naice! Found something fancy?

Well, actually I did.  After signing up for over a dozen awesome, great or plain terrible services, I found a really fancy service and spent a couple of hours getting the result looking clean and crisp.  Unfortunately, after spending those hours I discovered that this particular service only refreshes once every 24 hours.  I ended up settling for Yahoo! pipes, some kind of workflowy editor that seems to have no other use than to combine RSS feeds.

Which in a way, is exactly what I need. 🙂  And the pipes themselves look kind of fancy.

Image 031

So half a blog post to talk about how you wasted your Friday to end up with an RSS feed instead of a twitter bot?

No.

Half a blog post to talk about how I wasted my Friday to end up with an RSS feed in addition to a twitter bot.

That’s nice. Any downsides to this approach?

Well yes…. I got a bit lazy because Y!Pipes doesn’t have a copy&paste option, so now the bot that listens to the “LightSwitch news RSS feed”, doesn’t know the source of each item anymore.  Basically it  just tweets “#LightSwitch news: ….” now instead of tweeting “News by @AuthorTwitterHandle:  …”

Right… Well listen, thanks for blogging. We’ll give you a call…

Wait, there’s more!

  • RSS feeds are easy to plug into your favorite reader.  Or widget, like the new sidebar on my blog!
  • I can share the RSS feed now: http://bit.ly/lightswitchnewsRSS .  And if I find any good bloggers (feel free to make suggestions too!) I can simply update the Y!Pipes schema and you get all the new content too!

Smart!

And… Boring.  

Mostly boring.

So listen man, unless there’s some code goodies to be found in this post, I’ll be on my way.

Ok… Here you go 🙂

namespace RssDomain
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.ServiceModel.DomainServices.Server;
    using System.ServiceModel.Syndication;
    using System.Xml;

    public class LightSwitchNewsItem
    {
        [Key]
        public string Id { get; set; }

        public string Subject { get; set; }

        public string Content { get; set; }

        public string FullArticle { get; set; }

        public DateTime PublishDate { get; set; }
    }

    public class RssDomainService : DomainService
    {

        private static IEnumerable<LightSwitchNewsItem> allItems;

        [Query(IsDefault = true)]
        public IEnumerable GetDefault()
        {
            if (allItems == null)
            {
                string url = @"http://bit.ly/lightswitchnewsRSS";
                XmlReader reader = XmlReader.Create(url);
                SyndicationFeed feed = SyndicationFeed.Load(reader);
                reader.Close();

                allItems = feed.Items.Select(
                               item => new LightSwitchNewsItem
                                   {
                                       Id = GetId(item),
                                       Subject = item.Title.Text,
                                       Content = item.Summary.Text,
                                       PublishDate = item.PublishDate.DateTime,
                                       FullArticle = GetLink(item)
                                   }
                               )
                               .OrderByDescending(x => x.PublishDate);

            }
            return allItems;

        }

        static int i;

        static string GetId(SyndicationItem item)
        {
            string id = null;
            if (!string.IsNullOrEmpty(item.Id))
            {
                id = item.Id;
            }
            else if ((id = GetLink(item)) == null)
            {
                id = "Unknown  " + (++i).ToString("000");
            }
            return id;

        }

        static string GetLink(SyndicationItem item)
        {
            var link = item.Links.FirstOrDefault();
            if (link != null)
                return link.Uri.ToString();
            return null;
        }
    }
}

My eyes! I’m not supposed to get ugly code in them!  What is that?

It’s the code you can copy&paste in a RIA domain service, then use it as an external data source in your LightSwitch projects.

Image 030

That is indeed pretty cool! 

Thanks!  But not as cool as Dissociative identity disorder.  But anyways, I’m off.  Cya man, and don’t forget:

Keep rocking LS!

You too!