March 2012

Volume 27 Number 03

Leading LightSwitch - Consume a LightSwitch OData Service from a Windows Phone application

By Jan Van der Haegen | March 2012

Microsoft recently released the consumer preview of Visual Studio 11, which contains the next version of LightSwitch, Microsoft’s youngest technology. In this article, I’ll explore a key feature of LightSwitch added in the beta release: the ability to easily create and consume Open Data Protocol (OData) services, which can in turn be consumed from any client, including custom Windows Phone applications.

Creating OData Services with Microsoft LightSwitch 11

Because OData has been so widely adopted, knowing how to consume OData services in your application or expose your application’s data is quickly becoming a must-have skill for developers. As with most software problems, it’s the tooling that separates the heroes from the zeroes, and with Visual Studio LightSwitch 11, Microsoft has made sure you’ll be a hero.

If you’ve never created a LightSwitch application before, take a leap of faith and dive in headfirst with me; I’ll explain the key concepts as they come up.

Start by creating a new LightSwitch application in your .NET language of choice (as long as that choice is either Visual Basic .NET or C#), as shown in Figure 1. Because I’m creating an OData service that will be used in a fictional social media platform designed for bartenders, I named the application Bartender.Server. Open access to the data is what helped social media platforms such as Twitter and Facebook grow; so, I figure, it could provide the same boost for my Bartenders network.

Creating a LightSwitch Application
Figure 1 Creating a LightSwitch Application

LightSwitch has two clear mission objectives: to be the easiest way to create forms-over-data line-of-business (LOB) applications, and to be the easiest way to create Representational State Transfer (REST) data services. It should thus come as no surprise that the LightSwitch start screen (Figure 2) suggests Start with data. Click on the Create new table link, which will take you straight to the entity designer.

The LightSwitch Start Screen
Figure 2 The LightSwitch Start Screen

For this article, I’ll create just one entity: Cocktail (Figure 3). A cocktail, in its simplest form:

  • Has a catchy Name.
  • Often has an Origin, a country or place where the cocktail was created for the first time.
  • Has a short Description. A String has a default maximum of 255 characters in LightSwitch, which might not be sufficient for this use case. By pressing F4 with the Description property selected, you’ll see the designer properties of this property. Increase the maximum length as you see fit.
  • Has a Recipe. Experience has taught me that experimental cocktails tend to be created really late at night, so the exact recipe might not always be remembered and should be created as a non-required property in your system.
  • Has a popularity Rating, expressed in a percentage of users who like the cocktail.

Creating the Cocktail Entity
Figure 3 Creating the Cocktail Entity

Although the LightSwitch entity designer looks a lot like a database table designer, conceptually you should really think of it as a business object designer. By setting the Type of a property, you’re not merely dictating how LightSwitch should store the values—you’re also providing clues on how the default validation should work, or what the preferred control is to visualize that property. E-mail addresses, Web addresses and phone numbers, for example, are different business types in the LightSwitch entity designer that are all stored as the same data type (NVarChar).

In the editor, you should always use the singular form of the entity; LightSwitch handles the job of inferring the plural version where needed.

Adding custom logic is possible in any of the many code extension points that LightSwitch provides, all to be found by expanding (not clicking) the Write Code button (see Figure 4).

Code Extension Points
Figure 4 Code Extension Points

You could, for example, use this to implement the business logic that prevents the deletion of any cocktail with a rating greater than 80 percent. To do so, you’d replace the content of the method that’s generated when you click the Cocktails_Deleting entry from the dropdown menu with the code in Figure 5.

Figure 5 Business Logic to Prevent Deletion of a Popular Cocktail

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Security.Server;
namespace LightSwitchApplication
{
  public partial class DrinksService
  {
    partial voidCocktails_Deleting(Cocktail entity)
    {
      if (entity.Rating > 0.80m)
        throw new InvalidOperationException("Sorry, you cannot delete cocktails                   
          with a rating higher than 80%");
    }
  }
}

After saving all open documents, take a look at Solution Explorer. LightSwitch, by default, offers a Logical View of your solution, showing that you’ve created one data source called ApplicationData, which corresponds to the built-in SQL database that will be created for you by LightSwitch. This is the name that will be used for the data service, so you’ll want to change it to something more descriptive—Drinks, for example, as in Figure 6.

Naming Your Data Source
Figure 6 Naming Your Data Source

By default, LightSwitch also creates a Silverlight Out-of-Browser application that allows the end user to interact with your OData services. If you open the properties of your LightSwitch project and change the Application Type to a Web Application (see Figure 7), however, it will be easier to locate and inspect your service endpoint when debugging.

Choosing the Application Type
Figure 7 Choosing the Application Type

Now your OData service is ready to be used or even deployed, but it contains no initial data. Because LightSwitch offers an easy way to create forms-over-data, let’s add a screen that can be used to seed the service manually with some sample data. Right-click on the Screens item in Solution Explorer and select Add new Screen. In the editor that pops up (Figure 8), select the List and Details Screen template on the left, and bind it to the Cocktails by selecting it from the Screen Data combo-box on the right. LightSwitch will use a combination of this screen template, conventions and other metadata you designated earlier in the entity designer to create a screen at run time.

Creating a Screen for Adding Data
Figure 8 Creating a Screen for Adding Data

Press F5 to build and start debugging the application, and the default Web browser will open and display a Silverlight 5 client, ready to add the first cocktail to your data service, as shown in Figure 9. You add data by pressing the green Add icon above the list, entering values for the required fields at minimum (their labels will be bold to visually indicate which fields are required), and hitting the Save button in the Ribbon bar.

Adding Data
Figure 9 Adding Data

More importantly (for the scope of this article, at least), this means that LightSwitch has generated an OData service called “Drinks” behind the scenes, which you’ll find at an endpoint hosted at the same root address of the site you’re browsing now (see Figure 10). The exact URL of the endpoint should be similar to https://localhost:6506/drinks.svc/, though the port number (6506) will likely be different on each run.

Finding the OData Service
Figure 10 Finding the OData Service

Note that in a Web browser you can append any of your entities to that URL to request that data be set in the Atom format, which is what you’ll see if you browse to https://localhost:6506/drinks.svc/Cocktails. Here’s a tip that might save you a major headache one day: The entire URL is case-insensitive, except for the path to the collection—that is, the name of the entities.

Don’t close your browser, as this will end your server application’s debug session, which you’ll probably want to abuse as an IIS replacement for testing the client, instead of having to deploy the service to test the client.

Consuming OData Services in a Windows Phone Application

Realistically, a bartender might need to look up a cocktail when a customer is standing at the bar, looking at the bartender and waiting for the drink. Chances are, the bartender will not have an available PC or tablet that can run Silverlight right at that moment. For those situations, a Windows Phone application that provides a quick view of the most popular cocktails could be a lifesaver.

The Visual Studio 11 Consumer Preview currently doesn’t include support for Windows Phone development, so you’ll need to create a new project in Visual Studio 10 to finish this scenario.

Create a new Windows Phone application called Bartender.Mobile to connect to the running OData service, as shown in Figure 11.

Creating a Windows Phone Application
Figure 11 Creating a Windows Phone Application

Now add a service reference to the OData service by right-clicking on the project references and selecting Add Service Reference from the dropdown menu, as shown in Figure 12.

Adding a Service Reference
Figure 12 Adding a Service Reference

If you enter the URL you saw in the default Web browser, https://localhost:6506/drinks.svc/, and click the Go button, Visual Studio looks for the OData service and shows that it found the Cocktails entityset at the Drinks endpoint, as shown in Figure 13. In the same popup, change the Namespace that will be used by Visual Studio for the generated proxies to Bartender.ServerReference.

Configuring the Service Reference
Figure 13 Configuring the Service Reference

Apart from renaming the application title and page title, you’ll have to make only two small modifications to the default view:

  • Add a Listbox named CocktailsListBox that will hold all of the cocktails.
  • Add a single IconButton to the application toolbar and an event handler for that IconButton’s click event.

Figure 14shows the code that takes care of this.

Figure 14 Modifying the View

<phone:PhoneApplicationPage
  x:Class="Bartender.Mobile.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
  FontFamily="{StaticResource PhoneFontFamilyNormal}"
  FontSize="{StaticResource PhoneFontSizeNormal}"
  Foreground="{StaticResource PhoneForegroundBrush}"
  SupportedOrientations="Portrait"Orientation="Portrait"
  shell:SystemTray.IsVisible="True">
  <Grid x:Name="LayoutRoot"Background="Transparent">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="TitlePanel"Grid.Row="0" Margin="12,17,0,28">
      <TextBlock x:Name="ApplicationTitle" Text="BARTENDER"
                 Style="{StaticResourcePhoneTextNormalStyle}"/>
      <TextBlock x:Name="PageTitle" Text="cocktails" Margin="9,-7,0,0"                 
                 Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
    <Grid x:Name="ContentPanel"Grid.Row="1" Margin="12,0,12,0">
      <ListBox x:Name="CocktailsListBox"ItemsSource="{Binding }">
        <ListBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Vertical">
              <TextBlock Text="{Binding Name}"
                FontSize="{StaticResourcePhoneFontSizeExtraLarge}" />
              <TextBlock Text="{BindingDescription}"                     
                FontSize="{StaticResourcePhoneFontSizeSmall}" />
            </StackPanel>
          </DataTemplate>
        </ListBox.ItemTemplate>
      </ListBox>
    </Grid>
  </Grid>
  <phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBarIsVisible="True" IsMenuEnabled="True">
      <shell:ApplicationBarIconButton
        IconUri="/Images/appbar_button1.png" Text="Delete"
        Click="ApplicationBarIconButton_Click"         
      />
    </shell:ApplicationBar>
  </phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>

When you pasted the URL to the OData service in the Add Service Reference dialog, Visual Studio used only the service description to create the required proxy classes. However, when using those classes, you still have to point the proxies in the direction of the actual server, which is why the first variable in the class contains the (hardcoded) URL to the OData service again (see Figure 15).

Figure 15 Interactions Between the Server and the View

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Data.Services.Client;
namespace Bartender.Mobile
{
  public partial class MainPage : PhoneApplicationPage
  {
    private static readonly Uri Drinks_Uri = 
        new Uri("https://localhost:6945/drinks.svc/");
    private DataServiceCollection<Bartender.ServerReference.Cocktail> 
        cocktails;
    private Bartender.ServerReference.Drinks drinks;
    public MainPage()
    {
      InitializeComponent();
      drinks = new Bartender.ServerReference.Drinks(Drinks_Uri);
      cocktails = new 
          DataServiceCollection<Bartender.ServerReference.Cocktail>(drinks);
      cocktails.LoadCompleted += 
          new EventHandler<LoadCompletedEventArgs>(
      cocktails_LoadCompleted);
      LoadDrinks();
    }
    public void LoadDrinks()
    {
      var allCocktailsQuery = from c in drinks.Cocktails select c;
      cocktails.LoadAsync(allCocktailsQuery);
    }
    void cocktails_LoadCompleted(object sender, LoadCompletedEventArgs e)
    {
      if (cocktails.Continuation != null)
        cocktails.LoadNextPartialSetAsync();
      else
        this.ContentPanel.DataContext = cocktails;
    }
    private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    {
      Bartender.ServerReference.Cocktail cocktailToDelete
        = this.CocktailsListBox.SelectedItem as 
        Bartender.ServerReference.Cocktail;
      if(cocktailToDelete != null)
      {
        drinks.DeleteObject(cocktailToDelete);
        drinks.BeginSaveChanges(DeleteCocktailHandler, cocktailToDelete);
      }
    }
    private void DeleteCocktailHandler(IAsyncResult result)
    {
      drinks.EndSaveChanges(result);
      LoadDrinks();
    }
  }
}

Other globally scoped variables include a DataServiceCollection (found in the System.Data.Services.Client namespace) of Cocktails and a reference variable to the data source Drinks.

In Silverlight, there’s no Microsoft .NET Framework implementation to load an OData service’s entityset synchronously. That’s why you’ll find quite a lengthy portion of code in the constructor, LoadDrinks and cocktails_LoadCompleted methods to fetch all the data from the server and bind it to the CocktailsListBox. This process should be much easier to implement with the new async and await language constructs in the .NET Framework 4.5.

Just for testing purposes, add similar asynchronous logic in the event handler of the Remove button, which posts a delete message to the server for the currently selected cocktail, if any.

Pressing F5 to build and start debugging the application displays the beautiful result in Figure 16.

The Windows Phone Bartender Application
Figure 16 The Windows Phone Bartender Application

Wrapping Up

The result is simple but effective (from a Metro design point of view) in showing the key information about the cocktails you exposed via the OData REST service. If you press the delete button on random cocktails, there’s a one-in-five chance that Visual Studio 11—which should still be running to host your OData service—will prompt for your attention because you attempted to delete a cocktail with a rating greater than 80 percent, which violates the business logic you wrote (see Figure 17).

Trying to Delete a Popular Cocktail Produces an Error Message
Figure 17 Trying to Delete a Popular Cocktail Produces an Error Message

My source code doesn’t adhere to any Model-View-ViewModel (MVVM) best practices, enabling me to present the implementation in a single sourcecode file. On the Web you can find a number of great articles, both on consuming OData from .NET clients and on MVVM and data binding (including Jesse Liberty’s articles—Windows Phone Data Binding and Your First Windows Phone Application—which are scoped to Windows Phone development in particular).

The sample created in this article is easy to modify, and I took the time to do some additional experimenting with the application. Feel free to do the same and discuss it in the comments section or on my blog at janvanderhaegen.wordpress.com. Here are some possibilities:

  • Extend the client with a more appealing ItemTemplate for the selected item to show Recipe, Rating and Origin.
  • Extend the client with support for creating new and editing existing cocktails. Add an e-mail address property to store the address of the person who created the cocktail. Although that e-mail will be exposed as a string on the OData contract, try to identify if and how the service respects the underlying business types.
  • LightSwitch can help you consume OData services as well. You’ll find a list of producers that expose data via OData services at OData.org. Try to find one that allows you to search for an image for each cocktail, and add a cross-data-source, many-to-one reference between the pictures and cocktails. Try to get those images to show on the Windows Phone client.

Finally, here are some key points to take away from this introduction:

  • OData services can be used to export your business data via OData. Not only do they supply a view into your data, they offer basic Create, Read, Update, Delete (CRUD) operations that respect your business logic and user permissions.
  • Given some practice, you’ll be able to use Microsoft Visual Studio LightSwitch 11 as a tool to create OData services in no time, and publish them to IIS or Azure. LightSwitch has clear extension points where you can implement your custom business logic or enforce user permissions, a large set of common business types that each come with their own default validation and the ability to create reusable business types that fit your needs.
  • Though performing CRUD operations on OData services is easy, Silverlight’s implementation only allows asynchronous calls, which adds a bit of overhead to the amount of code needed.

Jan Van der Haegen  is a green geek who turns coffee into software. He’s a loving husband, a proud scrum master of an international team at Centric Belgium, and so addicted to learning about any .NET technology—Visual Studio LightSwitch in particular—that he maintains a blog on his coding experiments at janvanderhaegen.wordpress.com.

Thanks to the following technical expert for reviewing this article: Beth Massi