Leave my endpoints alone!

By default, LightSwitch apps expose the server tier by one public OData endpoint per data-source. Both the desktop and the HTML client make direct calls to these .svc urls to retrieve the data they need.

So here’s an interesting question I got this afternoon: this means that all data is publicly visible?

Well, yes and no. The endpoints are publicly visible, however when you access them, the LightSwitch (actually asp.Net) framework’s authentication and authorization will kick in to protect the data (authentication determines if you can access the endpoint, authorization determines if you can access that particular entity set and what records you can access).

However, there’s some cases where you want to protect your endpoints even more, for example when you’re forced to build an application without authentication, or if you want to prevent a user with a valid username&pw (or windows credentials) to access the data from outside your own apps.

In that case, you can add a custom IHttpModule. This would include registering the module in your web.config…

  <system.webServer>  
    <modules>
      <add name="CustomHeaderModule" type="StrongNamespace.HttpModules.CustomHeaderModule"/>
    </modules>

and adding whatever code you want to your server project…

using System;
using System.Web;

namespace StrongNamespace.HttpModules
{
    public class CustomHeaderModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.PostReleaseRequestState += new EventHandler(application_PostReleaseRequestState);
        }

        public void Dispose()
        {
        }

        void application_PostReleaseRequestState(object sender, EventArgs e)
        {
            if (HttpContext.Current.Request.Url.ToString().ToLower().Contains(".svc") 
                && !HttpContext.Current.Request.Url.ToString().ToLower().Contains("authenticationservice.svc")))
            {
                var referrer = HttpContext.Current.Request.UrlReferrer;
                if (referrer == null)
                    throw new Exception("Not allowed to access this data"); 
                else if (referrer.Host != HttpContext.Current.Request.Url.Host)
                    throw new Exception("Not allowed to access this data"); 
            } 
        }
         
    }
}

The above sample will block any calls to the .svc endpoints (your OData endpoints that expose your data) if the call is not made from a site within the same domain.

This hasn’t been production tested yet (and honestly you’ll probably need to customize the business rules a bit) but at first sight seems not to interfere with the LS desktop (OOB or inB) or HTML client, but do blocking any other calls.

It’s not really ‘web-friendly’ either, in production code I’d suggest rewriting the response to a 403-forbidden instead. But then again who gives a fuck about http status codes… (*grabs popcorn*)

 

Keep rocking LS!
Jan

Loading of collections in the desktop client on a ‘need to see’ basis.

I just spent my Saturday afternoon on a really fun puzzle: loading data on a ‘need to see’ basis. This code hasn’t been production tested at all (use at own risk), but is way too fun not to share.

Challenge

The LightSwitch desktop application can sometimes feel really sluggish, this is partly because collections on a screen ‘execute automatically by default’, this means that they are loaded automatically if they are used in the UI, even if that part of the UI is not visible (for example if the grid is on a tab that is not active at the moment). So here’s the challenge: can we speed up the application by loading data on a ‘need to see’ basis? Can we make our application intelligent enough so that data is loaded if and only if it needs to be visualized?

I have a screen that contains a couple of collections, let’s say that Foo has a one-to-many relationship with Bar and I made a list-detail screen with three tabs, one containing the Foo’s details, one containing the Foo.Bars as a Data Grid, and one containing the Foo.Bars as a List.Image 247Or at runtime:Image 248

When I go to the Foos List Detail, using Fiddler or another network sniffing tool, I can see that both the Foos, and the first Foo’s.Bars are automatically loaded.Image 251

(Well: obviously, because in the screen designer both the Foo and Bars collection have their ‘Auto Execute Query’ selected…)Image 243

Making your application smarter

Add a new class to your client project, named ‘LoadOnDemandExtensions’. Copy the source for that class from this gist.  It’s in C#, but here’s a good link for our VB friends.

(For those of you that actually clicked the VB link, I’m sorry. That was mean. To make it up, I posted the VB code at the bottom of that same gist 😉 )

Step 2: right-click on the client code in Solution Explorer, hit ‘View Application Code’ to open your client application code. In the Application_Initialize method, write:

C#
    public partial class Application
    {
        partial void Application_Initialize()
        {
            this.LoadVisualCollectionsOnDemand(); 
        } 
    }
VB
    Public Class Application

        Private Sub Application_Initialize() 
            Me.LoadVisualCollectionsOnDemand() 
        End Sub
    End Class

End of blog post.

No, really!  Press F5 and see how your application is behaving when it starts up now:Image 249

Even though the ‘Auto Execute Query’ is still on for Bars, the Lightswitch application didn’t load them because they aren’t visible at the moment. As soon as you hit the ‘Bars’ tab:

Image 250

 What is this sorcery?

The LoadVisualsOnDemand method will dig in the LightSwitch extensibility model to grab a hold of the NotificationService. It’ll ask that NotificationService to be notified whenever a screen is opened.

When a screen is opened it’ll loop over every collection on that screen and use some nifty hacking techniques (reflection is forbidden in SL in-browser sandboxed applications, but there’s a little loophole) to turn off ‘Auto Execute Query’ on the loader of each collection.

Also, it will search the screen for any ‘ContentItem’ (that is the right-hand side of your screen designer, which looks like a ‘visual tree’) that is displaying a collection (in reality this’ll be your List, Data Grid, or SuperDynamicGrid really). It will ask to be notified whenever such a control becomes visible/hidden. As soon as this happens, it’ll re-hack the VisualCollection to turn the ‘Auto Execute Query’ back on/off based on visibility.

This results in your collection becoming loaded, working (apparently paging, sorting and quicksearch do not work using other screen performance tips) and staying in sync (for example, you click on another Foo, this causes the Bars to be reloaded)

tl;dr; LightSwitch will now ignore your ‘Auto Execute Query’ attempts and turn that on/off depending on visibility.

Noteworthy caveat

Bugs incoming! This method will control the ‘Auto Execute Query’ property on all collections, every screen. However, some of your code might already depend on the implicit assumption that a screen collection is loaded. Screen your screen code files, and if you spot a spot where you access a collection in code, make sure it’s loaded using this other nifty extension method.

C#
        partial void Method_Execute()
        {
            this.Bars.LoadIfNeeded();
            var count = this.Bars.Count; //for example
        }
VB
        Private Sub Method_Execute()
            Me.Bars.LoadIfNeeded()
            Dim count = Me.Bars.Count 'for example
        End Sub

But what if…

What if I don’t want this behavior on every screen?

Instead of turning this on on an application level, you can activate it on a screen-by-screen basis.

On those screens where you need it, turn off the ‘Auto Execute Query’ manually on each collection. Then, write this in the screens _Activated code:

C#
        partial void FoosListDetail_Activated()
        { 
            this.LoadCollectionsOnDemand();
        }
VB
        Private Sub FoosListDetail_Activated()
            Me.LoadCollectionsOnDemand()
        End Sub

What if I don’t want this behavior on every collection?

Instead of turning this on on an application or on a screen level, you can activate it on a collection-by-collection basis.

On those screens where you need it, turn off the ‘Auto Execute Query’ manually on those collections. Then, write this in the screens _Activated code (repeat for every collection):

C#
        partial void FoosListDetail_Activated()
        {
            this.Bars.LoadOnDemand();
        }
VB
        Private Sub FoosListDetail_Activated()
            Me.Bars.LoadOnDemand()
        End Sub

 

Keep rocking LS!

(But only the visible parts)

Jan

 

 

 

Addendum: Multi-tenant LightSwitch applications: favor convention over configuration.

Wut wut, did I not already cover the generic multi-tenant filter in my previous blog post? Well how observant of you, dear reader, I most certainly did. However, in this post I’d like to elaborate a bit about a hidden ‘convention’ in PowerProductivityStudio.Server, the nuget package that you install on your LightSwitch server project to weave generic code entry points into your application.

See, when we (me and my alter ego) initially built PPS, we already had the idea in mind to use these generic code entry points to facilitate multi tenancy. Multi-tenancy is where a single deployed instance of a (LightSwitch) app is used by multiple groups of users (each group is a tenant), who share the hosting costs of the server and the database. This means that their data is in the same tables, and must thus generally be filtered so that tenants cannot view, let alone alter, data that belongs to another tenant.

Additionally, we figured that some default data would sometimes be common between different tenants as well. This data can be seen by any of the tenants, but changed only by the administrator.

In a nutshell, we needed an easy way for any LightSwitch project to:

  • filter data based on the TenantId of that data
  • show data that belongs to tenant “0” (default, shared data)
  • prohibit any changes/creations to data that belongs to tenant “0” (default, shared data)
  • make sure when a user creates a record, that data is correctly labeled with the correct TenantId
  • make sure when a user tries to update or delete data that belongs to a different tenant, this is blocked by the application.

Now, if these requirements sound like something you require (pardon my English vocabulary on Mondays…), you’ll be glad to find out that PPS has a hidden convention.

Here’s how that works:

Step one: add an integer “TenantId” column to any data that needs filtering, for example:

Image 183

Note: I suggest turning off ‘Show by default’ and setting the TenantId property as nullable. This way, the client will not ever have to bother with the property.

Now, just as a test, go to the Project entity where you added the TenantId property, and hit the “Write Code” > “Projects_Filter” button.

Image 190

A method stub will be generated that has one argument, and a code comment with a suggestion on how you can implement a filter here.

Put a breakpoint in the generated method stub and run the application. By default, you’ll notice that the ‘Filter’ argument is null…

Image 191

Instead of manually coding a lambda expression that filters records though, we’re going to use a convention in PowerProductivityStudio…

Wait, in whut whut?

Step two: install the PowerProductivityStudio.Server nuget package on your LightSwitch server project. PPS is a nuget package that contains a code weaving task. This task will add generic ‘code entry’ point to your application, so you can write code when ‘any entity is created/updated/deleted/queried’, …  This way you can create a generic filter at runtime, like in my previous post. However instead of writing a generic filter, we’ll take it one step further than my previous post again.  To install PowerProductivityStudio.Server, right-click on your server project and select ‘Manage nuget packages’. Search for ‘PowerProducitivityStudio.Server’ and install it.

Step three: tell PPS about your current tenant. To do this, add any class to your LightSwitch server application and implement the PowerProducitivityStudio.Extensibility.IMultiTenantService interface, for example:

using System.Web;
using Microsoft.LightSwitch;

namespace LightSwitchApplication
{
    public class MyMultiTenantService : PowerProductivityStudio.Extensibility.IMultiTenantService
    {
        public int GetCurrentTenantId()
        {
            return 1;
        } 
    }
}

Step four: launch your application!

If you still have your breakpoint in the ‘Projects_Filter’ method, you’ll notice that the query no longer is ‘null’, but actually has a valid ‘Multi-Tenant-Filter’ now…

Image 192

Succes? Let’s see…Image 184

All done… We just found an easy way for any LightSwitch project to:

  • filter data based on the TenantId of that data
  • show data that belongs to tenant “0” (default, shared data)
  • prohibit any changes/creations to data that belongs to tenant “0” (default, shared data)
  • make sure when a user creates a record, that data is correctly labeled with the correct TenantId
  • make sure when a user tries to update or delete data that belongs to a different tenant, this is blocked by the application.

Too easy? To short of a blog post?

Well, I could elaborate a bit more about my progress in learning Spanish (I can conjugate 12 verbs now, and know 27 different ways to order a beer), or I could help you a bit with that multi-tenant filter implementation.

First suggestion I would have for your multi-tenant filter (I know you’re not interested in my Spanish progress) would be to not hardcode it to “return 1;”.  You could set up a table that stores all tenants and their corresponding HostURL (for SharePoint provider hosted apps, like in the last post), or add a related table that stores which login (/user) belongs to which tenant.

Then, when PPS asks you for the tenant, just look that up using the ServerApplicationContext:

using System.Web;
using Microsoft.LightSwitch;

namespace LightSwitchApplication
{
    public class MyMultiTenantService : PowerProductivityStudio.Extensibility.IMultiTenantService
    { 
        public int GetCurrentTenantId()
        {
            var user = ServerApplicationContext.Current.DataWorkspace.ApplicationData.Users.Where(u => u.Login == ServerApplicationContext.Current.Application.User.Name).Single();
            return user.Tenant.Id;
        } 
    }
}

Sweet, but this will bog your system down quite a bit, since any data that is being retrieved/edited, will result in at least one extra call. Below is a revised version that will cache the current tenant id for a particular user in the HttpContext.Current.Cache. In addition, it also takes scenarios in mind where there might not be an existing ServerApplicationContext (if you use Web API for your fancy word document generation, for example), and shaves some milliseconds of the query by retrieving the User and it’s Tenant at the same time.

using System.Web;
using Microsoft.LightSwitch;

namespace LightSwitchApplication
{
    public class MyMultiTenantService : PowerProductivityStudio.Extensibility.IMultiTenantService
    { 
        public int GetCurrentTenantId()
        {
            int tenantId;
            if (HttpContext.Current == null //can happen if you have custom Web API calls
                || HttpContext.Current.Session["CurrentTenantId"] == null)
            {
                if (ServerApplicationContext.Current != null)
                {
                    var user = ServerApplicationContext.Current.DataWorkspace.ApplicationData.Users.Include("Tenant").Where(u => u.Login == ServerApplicationContext.Current.Application.User.Name).Single(); 
                    tenantId = user.Tenant.Id; 
                }
                else //can happen if you have custom Web API calls
                {
                    using (var context = ServerApplicationContext.CreateContext())
                    {
                        var user = ServerApplicationContext.Current.DataWorkspace.ApplicationData.Users.Include("Tenant").Where(u => u.Login == ServerApplicationContext.Current.Application.User.Name).Single();
                        tenantId = user.Tenant.Id;
                    }
                }
                HttpContext.Current.Session["CurrentTenantId"] = tenantId;
            }
            else
            {
                tenantId = (int)HttpContext.Current.Session["CurrentTenantId"];
            }
            return tenantId;
        }
    }
}

Badam tsss!

Just one more thing: PPS weaves the multi tenant filter just before the call is made to your “Projects_Filter” or “WhateverEntity_Filter” method.  There’s good news and bad news to that timing: if the multi-tenant filter that PPS creates does not fit your needs, you can easily overwrite it with whatever custom code you want, and re-assign the filter parameter. The bad news: if you already have a filter implemented (let’s say you only want projects with an ‘a’ in the name to appear), you’ll need to merge your custom filter and the existing filter together:

Image 194

 

Now, if you’ll actually try this, you’ll get a compilation exception, because combining two expressions is not supported unless you add this little helper class somewhere in your server project  😉


//Shoutout to MBoros on stackoverflow
public static class ExpressionCombiner {
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> newExp)
{
// get the visitor
var visitor = new ParameterUpdateVisitor(newExp.Parameters.First(), exp.Parameters.First());
// replace the parameter in the expression just created
newExp = visitor.Visit(newExp) as Expression<Func<T, bool>>;
// now you can and together the two expressions
var binExp = Expression.And(exp.Body, newExp.Body);
// and return a new lambda, that will do what you want. NOTE that the binExp has reference only to te newExp.Parameters[0] (there is only 1) parameter, and no other
return Expression.Lambda<Func<T, bool>>(binExp, newExp.Parameters);
}
class ParameterUpdateVisitor : ExpressionVisitor
{
private ParameterExpression _oldParameter;
private ParameterExpression _newParameter;
public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(node, _oldParameter))
return _newParameter;
return base.VisitParameter(node);
}
}
}

Badam tsss again 😉

Sample project has been uploaded to my OneDrive, and as always…

Keep rocking LS!

 

Addendum: a generic way to create A LightSwitch SharePoint 2013 Multi-Tenant Provider-Hosted Application

A couple of months ago (is it August already, really?)Michael Washington published a detailed article on creating multi-tenant Cloud Business Applications.  Multi-tenancy is a hot subject in the SAAS world, because it allows a single deployed server instance to serve multiple groups of users (called tenants because they usually pay for that service), which cuts down hosting costs and makes software updates so much easier.

The article is awesome, and after wasting Michael’s time because I thought there was a small security issue (which there isn’t) gets my absolute official seal of approval, along with a great A- rating!

Wait, A-? Why not A+?  Well, the implementation relies on you writing a bit of code each time an entity is inserted, updated, or queried.  Multiply this by 200-300 entities and you have a lot of typing to do, and a lot of chances to forget a particular entity. Unfortunately though, since LightSwitch has no generic way built-in to write code when ‘any’ entity is inserted, updated, or queried, there’s no way to built-in way to save your keystrokes…

Smooth transition: faithful readers of my blog will already know that when I say: no ‘built-in’ way, a small hack is just around the corner…

PS: this hack works for non-CBA LightSwitch apps as well.

Part 1: making the code more generic.

The name of the game is simple: when a LightSwitch application gets compiled, we will use a custom MSBuild task to open the server assembly, interpret the IL, and weave in calls to a generic event handler, then write code in our generic events.

Save yourself the headache (trust me, there’s a lot of headaches) involved in the actual hacking, and install a nuget package called powerproductivitystudio.server on your server project instead.  This package will add a custom MSBuild task to open the server assembly when it gets compiled, which interprets the IL, and weaves in calls to a generic event handler for you.  If you have seen my recent development on app-stitch, you’ll understand we use it ourselves extensively to drive events that happen in your LightSwitch app to the app-stitch event processor.

What you have left to do, is add a class where you can handle the generic LightSwitch events.  The class can be added anywhere in your server project, it can have any name, as long as it implements ‘PowerProductivityStudio.Extensibility.IServerEventHandler’, it will be recognized by the PPS framework at runtime and the methods will be called appropriately.  Here’s an empty stub to get you started:

    public class MyGenericFilter : PowerProductivityStudio.Extensibility.IServerEventHandler
    {
        public void EntityCreatedEventOccured(Microsoft.LightSwitch.IEntityObject entityObject)
        { 
            //Gets called when eny entity is created on the server.
        }

        public void EntityPermissionRequestOccured(string action, string entityPluralName, ref bool result)
        { 
            //Gets called when the server is asked if a user has rights to view/edit/... an entity.
            //Set result to true or false.
        }

        public void EntityValidatedEventOccured(Microsoft.LightSwitch.IDataService dataService, Microsoft.LightSwitch.IEntityObject entity, Microsoft.LightSwitch.IValidationResultsBuilder validationResultsBuilder)
        { 
            //Gets called whenever an entity is validated on the server.
        }

        public void FilterRequestOccured(ref T originalFilter) where T : class
        { 
            //Gets called whenever a set of entities is retrieved, and could be filtered.
        }

        public void ServerEventOccured(string action, Microsoft.LightSwitch.IEntityObject entity, Microsoft.LightSwitch.IDataService dws)
        { 
            //Gets called whenever an entity is being inserted, updated, deleted or is inserting, updating or deleting.
        }
    }

To make Michael’s Task_Inserting and Tasks_Updating code work for every single entity, you can implement the last method as:

        public void ServerEventOccured(string action, Microsoft.LightSwitch.IEntityObject entity, Microsoft.LightSwitch.IDataService dws)
        {  
            if (action.EndsWith("Inserting") || action.EndsWith("Updating"))
            {
                if (entity.Details.Properties.Contains("HostURL"))
                { 
                    var hostUrl = "MyTenantID123"; //this.Application.SharePoint.HostUrl.Host;
                    entity.Details.Properties["HostURL"].Value = hostUrl;
                }
            }
        }

The code checks if the entity has a property called ‘HostURL’ (Sometimes, multi-tenant apps have a set of commonly used data, which will not have the ‘HostURL’ property), and will set your tenant’s ID (the SharePoint Host URL in Michael’s example) on inserts and updates.

Almost done, and your app is forever multi-tenant… The last task is to make sure every entity that has a property called ‘HostURL’, is filtered so that each tenant can only access his/her data.

Michael does this by creating a filter (a lambda expression) and assigning it in the Tasks_Filter method. We’ll do exactly the same, only write some generic code to create the lambda expression at runtime, in the FilterRequestOccured method:

        public void FilterRequestOccured<T>(ref T originalFilter) where T : class
        { 
            //T is Expression<Func<Entity, bool>>
            var entityType  = typeof(T).GetGenericArguments()[0].GetGenericArguments()[0];
            if(entityType.GetProperty("HostURL") == null)
                return;
            
            var hostUrl = "MyTenantID123"; //this.Application.SharePoint.HostUrl.Host;

            // Only allow users to see records from their own site by creating a filter (Function) at runtime
            // filter = e => e.HostURL == this.Application.SharePoint.HostUrl.Host;     
            var e = System.Linq.Expressions.Expression.Parameter(entityType, "e");  
            var myTenantId = System.Linq.Expressions.Expression.Constant(hostUrl);
            var hostURLProperty = System.Linq.Expressions.Expression.Property(e, "HostURL");
            var hostURLEqualsMyTenantId = System.Linq.Expressions.Expression.Equal(myTenantId, hostURLProperty);
             
            Type funcType = typeof(Func<,>).MakeGenericType(entityType, typeof(bool));
            var newFilter = System.Linq.Expressions.Expression.Lambda(funcType, hostURLEqualsMyTenantId, new[]{ e }) as T;

            originalFilter = newFilter;
        }

Congratulations, your app is now forever multi-tenant, and you can continue with Michael’s awesome post.

Part 2: app-stitch: multi-tenant business rules

Hold on to your hats though, there’s one more chapter to tell in the multi-tenant story: business rules.  Business logic is usually hard-coded in the application, but sometimes one tenant has slightly different rules than another tenant.

To answer this need, we’ve made sure that app-stitch supports multi-tenant scenarios as well, so that each tenant can only see his/her own rules, but more importantly, to make sure that a business rule only fires on actions on data that actually belong to the tenant.

Once you have app-stitch installed, open the folder appstitch/appstitch.LightSwitch and open the class ItemUserAndContextProvider.cs.  Each time app-stitch needs an AppStitchItem (rules/audited events/settings), information about the current user or a ServerApplicationContext instance, the ItemUserAndContextProvider class will provide just that.

Locate this piece of code:

                Microsoft.LightSwitch.Server.IServerApplicationContext context;
                using (GetServerApplicationContext(out context))
                {
                    user = new AppStitch.UserInformation();
                    user.FullName = context.Application.User.Identity.Name;
                    user.CanView = true;
                    user.CanEdit = true;
                    user.IsDebugAdmin = context.Application.User.HasPermission(Permissions.SecurityAdministration); 
                }

This is where your application creates an ‘AppStitch.UserInformation’ instance to give app-stitch information about the current user.  (Note: this is also the place where you could deny a user from creating or even viewing the app-stitch business rules)

What you have to do, is add the fact that this user belongs to a particular ‘group’.  Rules created by a user will only be visible to other users in the same group, and will only apply to events (data that changes, …) by users in the  same group.  For this example, we’ll say that each tenant is a separate group:

                Microsoft.LightSwitch.Server.IServerApplicationContext context;
                using (GetServerApplicationContext(out context))
                {
                    user = new AppStitch.UserInformation();
                    user.FullName = context.Application.User.Identity.Name;
                    user.CanView = true;
                    user.CanEdit = true;
                    user.IsDebugAdmin = context.Application.User.HasPermission(Permissions.SecurityAdministration); 

                    //add this:
                    user.GroupId =  "MyTenantID123"; //this.Application.SharePoint.HostUrl.Host;
                }

In summary…

app-stitch relies on a nuget package that creates generic code entry points in your application.  With a bit of generic code of yourself, you can make your data and your business rules multi-tenant-ready once and for all.  If you want to see a sample, here’s the bits. 🙂

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

 

 

Fetal Development Pictures: a custom collection control

This week, I’ve been working on a LightSwitch custom collection control (LS Desktop Extensibility Toolkit) that shows a collection of ‘sticky notes’ for a given entity.  It was a private project so I cannot share the code right now (give it some weeks), but I did take some screenshots that show you the fetal development process.

Image 008

Image 009

Image 010

Image 011

Image 012

Image 013

Image 014

Image 017

Image 023

Image 101

Image 102

Hopefully these spark your imagination, it’s a pity not more people share actual LightSwitch projects, finished or in progress.

Keep rocking LS!

 

Breaking MVVM: Change the background color of a control in a LightSwitch desktop app

A couple of months ago I was faced with the following hacking challenge:

One of our entities has pairs of properties: StreetName and StreetNameAdv, City and CityAdv, Name and NameAdv, …  For each of these pairs, the screen should clearly color the background of the xx property, if the value does not match the xxAdv property.

Or, put in pixels:
fieldhighlighting

If you happen to be familiar with the LightSwitch MVVM implementation, you’ll realize this is not a walk in the park, because the controls that form the View layer are tucked away in a LightSwitch theme extension.  Trust me, that’s one area you’ll want to stay away from unless you’re really experienced with LightSwitch extensibility.

So the easier way, in this case… Is to break our MVVM layering…

[Audience gasps]

Breaking MVVM: episode one: Finding ViewModel ContentItems bound to a particular property on the Model.

This code snippet contains an extension method to track all ContentItems that are displaying a particular property.  The first step is to retrieve an IScreenPresentationView from an IScreenObject.  We do this with the help of the ScreenViewService, a class intended to help out people with their custom shells.  From there, it’s a recursive walk down the children of this root, and find out who’s displaying what.


public static List<IContentItem> FindContentItems(this IScreenObject screen, IEntityProperty viewModel)
{
IPresentationScreenView screenView = VsExportProviderService
.GetServiceFromCache<IServiceProxy>().ScreenViewService.GetScreenView(screen) as IPresentationScreenView;
if (screenView == null)
{
throw new InvalidOperationException();
}
IContentItem currentView = screenView.ContentItemTree;
var alreadyFoundViews = new List<IContentItem>();
FindContentItems(currentView, viewModel, alreadyFoundViews);
return alreadyFoundViews;
}
private static void FindContentItems(IContentItem currentView, IEntityProperty viewModel, List<IContentItem> alreadyFoundViews)
{
if (currentView != null)
{
if (currentView.Details != null && currentView.Details.Equals(viewModel))
{
alreadyFoundViews.Add(currentView);
}
foreach (var child in currentView.ChildItems)
{
FindContentItems(child, viewModel, alreadyFoundViews);
}
}
}

Breaking MVVM: episode two: Finding ContentPresenters and their controls.

Once you have a ContentItem, it’s a walk in the park to get all associated presenters (the’View’ layer): call the GetAssociatedPresenters method.

Almost there.  What you have now is a bunch of IContentItemPresenters, which give you access to the Silverlight controls used by your selected LightSwitch theme via their .Visual property.  However, these Silverlight controls are usually a lot more complex than a mere ‘TextBox’ or ‘Label’ here and there.  Hence, you’ll have to take a recursive dive in the visual tree to find the real control you need to color:


public static IEnumerable<IContentItemPresenter> GetAssociatedPresenters(this IContentItem contentItem)
{
var internale = contentItem as IContentItemInternal;
if (internale == null)
return new List<IContentItemPresenter>();
return internale.AssociatedPresenters;
}
public static void SetBackground(this IContentItemPresenter presenter, Color color)
{
if (presenter.Visual != null)
{
Control el = presenter.Visual as Control;
//{System.Windows.Controls.Primitives.date}
TextBox tb = FindControlByType<TextBox>(el);
if (tb != null)
{
Border tbContent = FindControlByType<Border>(tb);
if (tbContent != null)
{
tbContent.Background = new SolidColorBrush(color);
}
}
if (el is DateTimePickerVisual)
{
TimeUpDown tu = FindControlByType<TimeUpDown>(el);
TextBox tbo = FindControlByType<TextBox>(tu);
Grid gr = FindControlByType<Grid>(tbo);
Border bo = FindControlByType<Border>(gr);
bo.Background = new SolidColorBrush(color);
}
}
}
private static T FindControlByType<T>(DependencyObject container) where T : DependencyObject
{
return FindControlByType<T>(container, null);
}
private static T FindControlByType<T>(DependencyObject container, string name) where T : DependencyObject
{
T foundControl = null;
//for each child object in the container
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
{
//is the object of the type we are looking for?
if (VisualTreeHelper.GetChild(container, i) is T && (VisualTreeHelper.GetChild(container, i).GetValue(FrameworkElement.NameProperty).Equals(name) || name == null))
{
foundControl = (T)VisualTreeHelper.GetChild(container, i);
break;
}
//if not, does it have children?
else if (VisualTreeHelper.GetChildrenCount(VisualTreeHelper.GetChild(container, i)) > 0)
{
//recursively look at its children
foundControl = FindControlByType<T>(VisualTreeHelper.GetChild(container, i), name);
if (foundControl != null)
break;
}
}
return foundControl;
}

view raw

ColorBackground

hosted with ❤ by GitHub

Mission accomplished!  Well, these code snippets are really only the ‘helper methods’ to help you break your MVVM layering.  Depending on your needs, you’ll need some additional code.  I have a sample that demonstrates the entire thing in action.  Feel free to grab it from my skydrive!  

Keep rocking LS!

Jan

A trio of self-explanatory exceptions

Good: there’s a Visual Studio extension that allows you to bind F1 (or another shortcut) to “open a new browser and search for the currently highlighted text”.  Saves me at least 24 keystrokes per day, or over 100k keystrokes before I get to retire.

Bad: sometimes you get an exception that looks extremely self-explanatory, but the obvious solution, nor any of the search result web pages seems to solve your issue.

Even worse: had three of those in one day.  I thought I would save someone else some trouble some day, and post about these three ‘self-explanatory’ exceptions, and explain what the description really meant to me.

OutOfMemoryException: Out of memory.

[OutOfMemoryException: Out of memory.]
   System.Drawing.Graphics.FromHdcInternal(IntPtr hdc) +1151157
   System.Drawing.Font.ToLogFont(Object logFont) +145
   System.Drawing.Font.ToHfont() +98
   Telerik.Reporting.Pdf.Fonts.TrueType.FontReader.GetFontData(Font font, Boolean& isTTC) +101

What is seems to mean: “You are out of memory.  Consider scaling out.”

What it really meant in my case: “You are trying to run a Telerik report or another operation that requires GDI+ from an Azure Web Site.  Azure Web Sites do not support GDI+. Consider deploying to an Azure Cloud Service instead, or… Ah, screw it, this exception message is too long, let me just throw you an out of memory exception instead.”

What I did: deployed to an Azure Cloud Service.  Sent the bill to Telerik.  No response yet.

Unable to find any implementation of the contract: ‘Microsoft.LightSwitch.BaseServices.Logging.Internal.ILoggingService’

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) Unable to find any implementation of the contract: 'Microsoft.LightSwitch.BaseServices.Logging.Internal.ILoggingService'

What is seems to mean: “Your IOC container, MEF, is missing an implementation of the ILoggingService.”

What it really meant in my case: “You tried to instantiate a ServerApplicationContext, but something went wrong.  Whatever went wrong will be logged, let me just get a logger from the MEF container that is created when you instantiate a ServerApplicationContext.  Oooo crap”.

What I did: found that some of my ServerApplicationContexts wasn’t properly being disposed of.  Always wrap your IDisposables in a using statement.

System.Security.SecurityException: Dialogs must be user-initiated

What is seems to mean: “You are trying to show a Print/Save/Open dialog from a sandboxed Silverlight application, but the user asked for no such thing.”

What it really meant in my case: “You are trying to show a Print/Save/Open dialog from a sandboxed Silverlight application, and the user did just click a Print/Save/Open button.  However, somewhere deeply buried in MSDN documentation it says that between the user instantiated event and the point where you show a dialog, there can only be a very limited amount of time.  The length of this time limit isn’t specified in an absolute number of milliseconds, but as ‘the number of milliseconds you really need, minus 5‘.”

What I did: removed all breakpoints, and split up the processing of the report and the actual printing in a two-step process.  This way, when the report is fully rendered, the user has to click a button “ok well print it then” which only invokes the actual print dialog.

Your turn, Dr. Watson.

LightSwitch and T4 code generation

If you find yourself writing ‘similar’ and ‘repetitive’ code, you might want to look into custom code generation.

Code generator tools generate their code based on some kind of meta-data.  LightSwitch is partly a code-generator tool itself: what you do as a developer in the Screen Designer, Query Designer or Entity Designer, is stored in LightSwitch Markup Language (lsml files, the meta-data that describes your application), and used to generate code for parts of your application at compile time, and to interpret other parts at compile time.

Because your LightSwitch application already has this meta-data-driven approach, it’s extremely easy to do additional code generation yourself!

I started by downloading some tools, Tangible T4 editor came highly recommended for code coloring and “File>New Item” templates.  After installation and VS reboot, I added a new file and from the Tangible section in the Add New File dialog, selected “Advanced” template.

This generates a sample tt file (the T4 template) which interprets a piece of xml to generate a .cs file (any file is supported).  In the tt file, just swap out the hardcoded xml to load the lsml files you need (the path to your lsml files might be different!):

	XNamespace  schema = "http://schemas.microsoft.com/LightSwitch/2010/xaml/model";
	string serviceLsml = System.IO.File.ReadAllText(
		System.IO.Path.GetDirectoryName(this.Host.TemplateFile) + "\\..\\TESS.DataWarehouse\\TESS.DataWarehouse.Server\\Properties\\Service.lsml");
	System.Xml.Linq.XDocument XmlDoc = System.Xml.Linq.XDocument.Parse(serviceLsml);

Then just use your XDocument wizardry to extract the information you need…

		foreach(var entityContainer in XmlDoc.Root.Descendants (schema + "EntityContainer"))
        {
			#>
				// Data source: <#= entityContainer.Attribute("Name").Value  #>
				<#

Practical example: although it was tempting to try to generate a WinForms application based on the client lsml, we found it more rewarding to use this in our reporting solution instead.  We have this one LightSwitch application which exposes a couple of views (BI sans BS).  Reports are ran from the server as well, using the ServerApplicationContext as a data source.  However, when an end-user wants to design a new, or alter an existing ‘report definition’, he/she can’t “access” the ServerApplicationContext in the end-user report designer.  As a workaround, we use T4 to generate a stand-alone class library with a simplified layout of our LightSwitch ServerApplicationContext, but which spits out random dummy data when accessed. This class library is then distributed alongside with an end-user report designer, so that they can design the reports.  When a report is uploaded, we simply swap out the ‘Dummy’ data source for one that accesses the ServerApplicationContext instead.  (Mega-Props to Paul Van Bladel for switching on the light here 😉 )

Here’s a fragment of the full T4 template, more likely to be used as an example than anything else.  As you can see, it generates a partial class, so that I can have some non-generated methods as well (like: CreateDummyData< T >).


<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Xml.dll" #>
<#@ Assembly Name="System.Xml.Linq.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
// Generated class, do not modify the class but the .tt file instead!!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace LightSwitchApplication{
[DataObject]
public partial class ReportSource{
<#
XNamespace schema = "http://schemas.microsoft.com/LightSwitch/2010/xaml/model&quot;;
string serviceLsml = System.IO.File.ReadAllText(
System.IO.Path.GetDirectoryName(this.Host.TemplateFile) + "\\..\\TESS.DataWarehouse\\TESS.DataWarehouse.Server\\Properties\\Service.lsml");
System.Xml.Linq.XDocument XmlDoc = System.Xml.Linq.XDocument.Parse(serviceLsml);
foreach(var entityContainer in XmlDoc.Root.Descendants (schema + "EntityContainer"))
{
#>
// Data source: <#= entityContainer.Attribute("Name").Value #>
<#
foreach(var entitySet in entityContainer.Descendants( schema + "EntitySet" ))
{
if(entitySet.Attribute("Name").Value == "Reports" ||
entitySet.Attribute("Name").Value == "ReportDefinitions")
continue;
#> [DataObjectMethod(DataObjectMethodType.Select)]
public IEnumerable< <#=entitySet.Attribute("EntityType").Value #> > <#= entitySet.Attribute("Name").Value #> ()
{
return GenerateDummyData< <#=entitySet.Attribute("EntityType").Value #> >();
}
<#
}
#>
<#
}
#>
}
<#
foreach(var entityType in XmlDoc.Root.Descendants ( schema + "EntityType"))
{
if(entityType.Attribute("Name").Value == "Report" ||
entityType.Attribute("Name").Value == "ReportDefinition")
continue;
#>
public partial class <#= entityType.Attribute("Name").Value #> {
<#
foreach(var property in entityType.Descendants(schema + "KeyProperty")){ createProperty(property, schema);}
foreach(var property in entityType.Descendants(schema + "EntityProperty")){ createProperty(property, schema);}
#>
}
<#
}
#>
}
<#+
void createProperty(XElement input, XNamespace schema) {
if(input.Descendants(schema + "Hidden").Any())
return;
#>
public <#+ createPropertyType(input, schema); #> <#= input.Attribute("Name").Value #> {get;set;}
<#+
}
#>
<#+
void createPropertyType(XElement input, XNamespace schema) {
var propertyType = input.Attribute("PropertyType").Value.Substring(1);
if(propertyType.Equals("String?")) { #><#= "string" #><#+ }
else {#><#= propertyType #><#+}
}
#>

The amount of LightSwitch and T4 resources online right now is … Well, just this post… Two: this post, and Paul Van Bladel’s material.  If you ever decide to wander on this lucrative T4+LightSwitch combination, please do share your LightSwitch love so that I and others can learn.

Keep rocking LS!

Jan

Detect if the browser will run your LightSwitch HTML app.

I just got a great tip from Michael Washington (yes, that awesome dude from that awesome website) that I wanted to post while I’m working on a large blog post about threading in LightSwitch (whoops),

If an end-user tries to open a LightSwitch application in a browser that is not supported, they are very likely to get stuck on the loading screen, with a little loading animation leading them to believe that the application is almost ready to rock&roll.  Unfortunately, a lot of browsers will silently swallow the JavaScript exceptions (or just show a minor warning icon with a message that makes no sense to end users anyways, at best), making the user wait in vain until the loading icon stops spinning.

One way to reduce the number of support tickets heading towards the internal IT department, is to prepend the loading of the LightSwitch JavaScript libraries with a check if JQuery mobile is supported.


<script type="text/javascript">
$(document).ready(function () {
var supported = $.mobile.gradeA();
if (!supported) {
$(".ui-icon-loading").hide();
$(".ui-bottom-load").append('<div class="msls-header">This browser is not supported. Please upgrade to a more recent, <a href="http://jquerymobile.com/gbs/">supported</a&gt; version.</div>');
} else {
msls._run()
.then(null, function failure(error) {
alert(error);
});
}
});
</script>

Small caveat: not all browsers that support JQuery mobile at A-grade, will be able to run LightSwitch HTML apps.  My WP7.8 for example is not running LightSwitch HTML apps correctly, although it will slip through the net of this check.  This means you can either deal with that small percentage by buying enough hardware to run every possible browser and manually cross-check every LightSwitch feature so that you can keep a completely bullet-proof browsers<>version matrix up to date, or keeping this maintenance-free check and letting the IT crowd handle the rest… (thanks Matthieu for getting me addicted :p)

tumblr_m3yz13kTE41rsxd68o1_500

 

Keep rocking LS!