Using the LightSwitch ServerApplicationContext API (Matt Evans)

The ServerApplicationContext API is a new feature in LightSwitch, available with Visual Studio 2012 Update 2 or later, which allows you to create entirely new ways to call custom business logic on the LightSwitch Server, using the same rich API you’re used to working with on the server tier. We previewed this API in the VS 2012 HTML Client Preview 2 release, but we’ve made a few tweaks since then, and this article discusses the API in a bit more depth.

Background

By default, the only way a client or service can communicate with the LightSwitch server is via the OData protocol, and only to EntitySets and Queries you’ve created in the Query designer. See LightSwitch as a Data Source.

We recently introduced a feature in the LightSwitch server which allows developers to create alternative entry points on the LightSwitch middle tier (a.k.a. the server). This is very handy if you want the server to communicate with clients that don’t understand OData, or you need to return data that isn’t shaped like one of your Entities. For instance, if you want to invoke some custom logic on the server, the solution until now has been the "command table" pattern, where you create an entity which is just a conduit for sending work requests to the server. Another common request we get is for a way to generate reports and interactive dashboards. Reports usually aren’t shaped like whole entities, but rather projections of entities and aggregates. To solve this sort of reporting problem, people have had to resort to cumbersome mechanisms like custom RIA services in order to be able to transfer non-entity data out of the server.

Eventually it would be nice to have a great inbox experience for reporting and for service operations. However, in the interim, we have introduced the ability for developers to use the LightSwitch API inside of their own web service endpoints. You can add any of the normal ASP.NET web assets to your Server project and create new ways of interacting with the LightSwitch server. You could create something quick and dirty like an ASP.NET Web Form, or something more powerful like a WCF Service or a Web API endpoint. The key point is that you decide the appropriate way you’d like to expose a new service, using the normal Visual Studio gestures for adding and working with those assets.

Technically, it has always been possible to add aspx pages and Web API calls to the LightSwitch server, but there was no easy way for you to use the LightSwitch API inside your custom entry points, so it wasn’t a great experience.

With the ServerApplicationContext API, we’ve made certain scenarios much easier, and we’re opening things up to your imagination.

A quick and dirty example

1. Create a new LightSwitch HTML / C# Application
2. Add a new Table, called "Customer". Give it two properties, "Name" and "BirthDate"
3. Add a new Browse Screen for the Customer Table
4. In Solution Explorer, change to "File View"

image

5. Select the Server project in Solution Explorer
6. Right click on the Server Project, choose "Add" and then choose "New Item"

image

7. Search for "web form"
8. Add a new asp.net web form named "MakeData.aspx"

image

9. Expand MakeData.aspx in the Solution explorer. Double click on the code-behind file (MakeData.aspx.cs)
10. Paste the following code into the Page_Load method:

C# Code:

protected void Page_Load(object sender, EventArgs e)
{
   
using (ServerApplicationContext context = ServerApplicationContext.CreateContext())
   
{
       
Customer c = context.DataWorkspace.ApplicationData.Customers.AddNew();
        c.Name = "Good Guy Greg";
        c.BirthDate = DateTime.Today;
       
context.DataWorkspace.ApplicationData.SaveChanges();
   
}
}     

VB Code – note that you’ll want to add "Imports LightSwitchApplication" at the top of your VB code files

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   
Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext()

        Dim c As Customer = Context.DataWorkspace.ApplicationData.Customers.AddNew() 
       
c.Name = "Good Guy Greg" 
       
c.BirthDate = Date.Today 
       
Context.DataWorkspace.ApplicationData.SaveChanges() 
     End Using       
End Sub

11. Make another web form called "ShowData.aspx". Add a using statement for "Microsoft.LightSwitch".
12. Put the following code into the Page_Load method

C# Code:

protected void Page_Load(object sender, EventArgs e)
{
   
using (ServerApplicationContext context = ServerApplicationContext.CreateContext())
   
{
       
foreach (Customer c in context.DataWorkspace.ApplicationData.Customers)
       
{
           
Response.Write(c.Id + " " + c.Name + " " + c.BirthDate + "<br>\r\n");
       
}
    }
} 

VB Code:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   
Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext()
        For Each c As Customer In Context.DataWorkspace.ApplicationData.Customers
            Response.Write(c.Id.ToString() + " " + c.Name + " " + c.BirthDate + "<br>" + vbCrLf)
        Next
   
End Using
End Sub

13. F5 your application
14. Note the URI of the app during F5; it should be something like http://localhost:12345/htmlclient/
15. Open a new browser tab, and manually type in the following uri: http://localhost:12345/MakeData.aspx Fix up the port number, as needed.
16. Open a new browser tab, and manually type in the following uri: http://localhost:12345/ShowData.aspx
17. Go back to the original tab, which shows the HTML client. Refresh this tab. You should see new data in your app.

If all went well, you should have seen your entity data in steps 15, formatted as uninteresting text, and again in step 16, inside the HTML client UI.

This example isn’t especially interesting, but shows that if you know ASP.NET development, you can now build arbitrary pages and other types of endpoints that read and write to your LightSwitch data, using the LightSwitch API.

API Overview

There are actually two primary ServerApplicationContext classes. One of them is strongly typed for your application, that is, it understands which data sources, entities, and queries are in your specific LightSwitch project. This is what you’ll use most of the time. It lets you write code like this:

C# Code:

using (ServerApplicationContext context = ServerApplicationContext.CreateContext())

    var v = from c in context.DataWorkspace.ApplicationData.Customers
            where c.Name.Contains("Matt")
           
select c;

       foreach (Customer c in v)
       
Response.Write(c.Name + "<br>\r\n");
   
}
}

VB Code

Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext()

    Dim v = From c In Context.DataWorkspace.ApplicationData.Customers
            Where c.Name.Contains("Matt")
           
Select c

       For Each c As Customer In v
       
Response.Write(c.Name + "<br>" + vbCrLf) 
    Next
End Using 

Note that because we are using the strongly typed model, our context has knowledge of the ApplicationData service and its Customers table, and all of the properties on the Customer entity.

Weakly Typed ServerApplicationContext

There is another ServerApplicationContext class which is weakly typed. It has no compile time knowledge of your entities or other project assets. However, it can access these items at runtime, using the Weakly Typed API. (Read more here). The weakly typed ServerApplicationContext is actually created by a call to the ServerApplicationContextFactory:

C# Code

using (IServerApplicationContext icontext = ServerApplicationContextFactory.CreateContext())
{
    var typ = icontext.DataWorkspace.SecurityData.GetAuthenticationType();
    if (typ.HasFlag(AuthenticationType.Windows) || typ.HasFlag(AuthenticationType.Forms))
   
{ i++;
   
}
}

VB Code – note you’ll need to add "Imports Microsoft.LightSwitch.Server" and "Imports Microsoft.LightSwitch.Security" at the top of your source file..

Using icontext As IServerApplicationContext = ServerApplicationContextFactory.CreateContext()
   
Dim typ = icontext.DataWorkspace.SecurityData.GetAuthenticationType()
    If (typ.HasFlag(AuthenticationType.Windows) Or typ.HasFlag(AuthenticationType.Forms)) Then
        i = i + 1
   
End If
End Using 

Suppose that you are writing some sort of extension module that enables generic reporting or import/export scenarios. You want to create a package that anyone can add to their LightSwitch application which will create new Web API endpoints that allow for exporting data as csv files. Because your module needs to work with any possible LightSwitch application, it has no strongly typed model to work with. However, it is straightforward to write weakly typed code which will enumerate the datasources, entitysets, key properties, and so on, allowing you to create a generic module that can operate in any LightSwitch application. The dynamic URL parsing and routing of Web API makes this an especially interesting scenario, e.g. suppose someone requests the following uri:

http://contoso.com/myLightSwitchApp/CsvExporter/Customers

It would be straightforward to write a generic module which implemented this CSV exporter as a Web API endpoint (CsvExporter), which would infer the EntitySet (Customers) to export based on the incoming URI.

As a side note, because the SecurityData data service is available in any LightSwitch application, that dataservice will be available strongly typed even on a weakly typed ServerApplicationContext, as seen in the example above.

Challenge: After you’ve completely read this article, take another look at the above example where I call ServerApplicationContextFactory.CreateContext. Will the variable i ever get incremented by this code? Why or why not?

Current vs. CreateContext

There are two items of interest on the ServerApplicationContext classes: the Current property and the CreateContext method. The Current property returns the currently in-scope ServerApplicationContext, if one exists. The CreateContext method creates a new ServerApplicationContext for your use.

Unlike DataWorkspaces, which can be "Stacked" so that many are simultaneously in scope, only one ServerApplicationContext can be present for a given logical request on the server. Each incoming request to one of the in-built LightSwitch server endpoints has its own ServerApplicationContext automatically created for the lifetime of the request, and which is used to service all activity for that request. If you were to try to create a second ServerApplicationContext when one was already present, you would get a ContextExistsException.

When you are creating your own entry points into the server, it is typically safe to simply call CreateContext without checking to ensure that Current is null first. This is because the normal LightSwitch server hasn’t been called yet; IIS and your code are handling the HTTP request routing and LightSwitch hasn’t had the opportunity to initialize anything on your behalf.

On the other hand, if you are inside of normal LightSwitch code on the server, like SaveChanges_Executing or Customer_Inserting, attempting to create a new ServerApplicationContext will always fail, because in these cases, the ServerApplicationContext that the LightSwitch save pipeline has created for its own use will already exist.

In almost all cases where you are defining the web entry point yourself (a webform, Web API, etc), to use a ServerApplicationContext you just do this:

C# Code

using (ServerApplicationContext context = ServerApplicationContext.CreateContext())

{

// my code goes here

}

VB Code: 

Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext()

‘ my code goes here

End Using

Authentication

Because the key usage scenarios for ServerApplicationContext involve creating new service endpoints on the LightSwitch server, by default, if your LightSwitch application is set to use authentication, ServerApplicationContext tries to enforce user authentication. Specifically, if in your web.config, the Authentication mode is Windows or Forms, when your code tries to create a new ServerApplicationContext by calling CreateContext, if there isn’t a valid authenticated user already on the HttpContext, your call to CreateContext will throw an exception.

If you know what you are doing and do not want this behavior, you can tell CreateContext to skip the authentication check, by calling it in the following way:

C# Code

using (ServerApplicationContext context = ServerApplicationContext.CreateContext(ServerApplicationContextCreationOptions.SkipAuthentication))

VB Code

Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext(ServerApplicationContextCreationOptions.SkipAuthentication)
‘ allow in unauthenticated users
‘ my code goes here
End Using 

The SkipAuthentication flag tells CreateContext not to do the authentication check.

Next Steps

There are some more complete end to end examples that use ServerApplicationContext, which you can read about here:

Note that these were written in the HTML Preview 2 timeframe, and so the location of the ServerApplicationContext classes is a little bit different. We’ve got more blog posts planned with some good reporting examples so stay tuned!

– Matt Evans, Tester, LightSwitch Team