Using the LightSwitch ServerApplicationContext API (Matt Evans)

Using the LightSwitch ServerApplicationContext API (Matt Evans)

Rate This
  • Comments 11

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

    Leave a Comment
    • Please add 7 and 2 and type the answer here:
    • Post
    • Nice article, but that last section on Authentication makes me worried.  Could the SkipAuthentication method result in the loss of data security?

    • Allen,

      The default option -- where your code doesn't pass the SkipAuthentication option -- enforces our authentication system for you.  But that may not be what you want in every case, so we give you an additional choice.

      If you use the SkipAuthentication option, creating and using the context will succeed without first establishing the identity of the user.  The SkipAuthentication option is present precisely for a scenario where you need to use the LightSwitch API but the caller will not be authenticated yet.  You can either do this authentication yourself, or you can proceed "anonymously"

      The lifetime of the ServerApplicationContext is limited to just the code inside the using {.. } block.  So even if you add one an endpoint that uses ServerApplicationContext with SkipAuthentication set, only that using block will be affected; nothing else in the app can be accessed in an unauthenticated way.

      Since you control the code inside of the using { ... } block, if you're using the SkipAuthentication option, don't write security sensitive code inside there.  Then there will be no trouble.

      Does that help?

    • That is somewhat reassuring but I still worry about the multiple ways the data can be accessed and the potential security risks it could compound.  I am not a security expert or a hacker, but these people are trained to circumvent security protections in ways that the original developers did not intend.  In essence, the Lightswitch team has potentially given these types of people more ways to get at my data, namely SQL, OData, and now the strong and weak ServerApplicationContext along with SkipAuthentication.  Some of my Lightswitch data, for instance, contains confidential patient medical records, which are required by federal law to be tightly secured.

    • Hi Allen,

      A LightSwitch application is no less secure than any other website. If you don't secure the server and use https for sensitive data transfer then you'll run into trouble no matter what technology you use. LightSwitch is making it easy for a developer to extend its services so that you can get a lot more ROI from your business logic in there if you need it. You don't have to use the feature if you don't understand the implications of the code.

      HTH,

      -Beth

    • Matt, thanks a lot for this excellent article !

      @Allen,

      I see you, almost in the same sentence making statement about potential security holes and " I am not a security expert...". Please, no offence, but... don't do this. In case you *are* a security expert, everyone will look forward to your detailed analysis. If you are not: ask questions, ask for clarifications,etc. .. but nobody is helped by tendentious statements.

      The above article is about server side application security processing. The developer (the owner) of the application has this context in full control and every action he takes there is his responsibility. For example: one of the security options that you can take server side is either to activate or to not active authentication. Well, if you as a developer would decide to NOT activate authentication, would this be a reason to state that the application framework is not secure ?

    • Something I couldn't find for awhile - we use AzMan through a service, so I added a service reference to the server tier called AuthService.  I couldn't find AuthService in the object browser until I realized it was LightSwitchApplication.AuthService.  

    • Sorry, I'm late to this article...it's very good, and thanks for it.  One question I had was regarding the statement about the weakly typed server application.  In it Matt mentioned "Suppose that you are writing some sort of extension module that enables generic reporting or import/export scenarios."

      When he says 'module' does he mean an http module, ala IIS?  Or something else?

    • Larry,

      I didn't specify a specific packaging/technology, but when I was writing the article, what I had in mind was a .dll you could add to your project; perhaps via NuGet.  I suppose that other things, like an http module, could also work.  If I remember correctly, there are some object initialization concerns when trying to use the LS apis from an HTTP module, so I would say that's definitely an advanced scenario.

      The idea is that by using the weakly typed API, you do not take any type or assembly references to a specific lightswitch app; only the Lightswitch framework.  This means that any code using the weakly typed API can be dropped readily into other lightswitch applications -- either as source code or as a binary component (e.g. assembly reference)

    • Matt, Thank you for this fantastic article!  I have been using Web API samples to do some server side coding in my apps but your article has clarified quite a few things!  Thanks :)

      I have a question about the actual authentication itself - if I wish to enforce it - it seems the only way is to pass on an authCookie in the actual request.  Is this correct?  

      Thanks again for a wonderfully clear and concise article :)

    • @Ankur,

      It depends on which overload you want to use.  

      If you use:

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

      Then you don't need to pass in any authentication at all.  We expect that you somehow handle authentication yourself in this case.  

      If you use the default overload:

      using (ServerApplicationContext context = ServerApplicationContext.CreateContext())

      Then yes, the client calling the service must have authentication sorted out.

      How this is done depends on the auth mode that the application is configured to use.  The valid choices for an LS app are None, Forms, Windows, and Sharepoint.

      None is the easiest case -- we don't require any authentication in that case.

      Windows is also an easy case - normally IIS negotiates the Windows identity of the client user before any LS code is called.  We will only further ensure that the windows user is in the Users table, if you've configured the "specific users" setting on the Permissions page.

      Forms is the most interesting case.  Forms authentication is typically done in two steps.  The first step is that the unauthenticated user is redirected to a logon page, where they must enter a username/password.  They submit that to the server and the server hands back a cookie.  Subsequently, the client will send the cookie back to the server; the server decodes the cookie to verify the user is authenticated.  This is normal ASP.NET forms auth.

      However, because this scenario requires manual interaction between a browser and a logon page, it isn't a good fit for hitting service endpoints directly.

      For this reason, when an LS app is configured for Forms auth, the Service Endpoints (OData services) will also accept HTTP BASIC auth.   This is so that apps which aren't interactive browsers can still consume our OData services.

      The System.Net Web&HttpClients support BASIC AUTH via the Credentials properties, so it is easy to program against our endpoints.

      Finally, the last auth mode is Sharepoint auth.  The LightSwitch HTTP Module handles sharepoint auth; you cannot access a service endpoint without a valid SharePoint auth token, which can only be acquired by being redirected from a sharepoint site.

    • Great article Matt!

    Page 1 of 1 (11 items)