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.

Advertisements

2 thoughts on “John Holmes and the case of the mysteriously missing LightSwitch record

  1. Jan, you have a great tech blog and have become quite the resource for serious LightSwitch developers. But this story highlights one of the reasons we had already decided to drop LightSwitch in favor of developing single-page apps with other methods. aurelia.io is definitely going to get spiked in the next few months as a possible answer.

    Anyways, yes – LS is great at hiding pieces of code of code away which dumbfounds by changing expected behavior. We find larger or more sophisticated applications that really push the LS boundaries become very difficult to maintain. In our opinion the resulting code does not document itself very well for other programmers to discover (or remember a year or two on). And finding, for example, a row level filter becomes a real process.

    Really looking forward to playing with aurelia.io but got one last LS project to finish up first…. Many thanks for all the useful LS info you’ve put together for the rest of us!!

    • Hey Rob,
      thanks for the flattering reply. I’ve always loved the promise of LS, but you correctly mention that in the long run, hiding the underlying technology becomes a counter-productive measure that negates all earlier the productivity gain in medium-large apps.
      So far, I’m having a lot of fun with aurelia. It’s not even in beta stage yet, but it is simply incredible! I plan to start blogging about it in about… 3 minutes from now 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s