Calling ASP.NET Web API from a LightSwitch Silverlight Client

Calling ASP.NET Web API from a LightSwitch Silverlight Client

Rate This
  • Comments 3

Before I left for a wonderful vacation camping in Lake Tahoe :) I showed you how you can use Web API with your LightSwitch middle-tier service in order to call stored procedures in your databases. I also showed you how we’ve improved database management in Visual Studio 2013 with the addition of SQL Server Data Tools (SSDT) support. If you missed them:

You can do all sorts of wonderful things with SSDT as well as with Web API. Starting in Visual Studio 2012 Update 2 (LightSwitch V3), we added the ability to use the ServerApplicationContext on the middle-tier so you can create custom web services that utilize all the business and data logic inside LightSwitch. This makes it easy to reuse your LightSwitch business logic & data investments and extend the service layer exactly how you want. (See this and this for a couple more examples).

I got a few questions about my last post where I showed how to call the Web API we created from a LightSwitch HTML client. Folks asked how to call the same Web API from the LightSwitch Silverlight client so I thought I’d show a possible solution here since many customers use the desktop client today. And although I’ll be using it in this post, it’s not required to have Visual Studio 2013 to do this – you can do it with VS2012 Update 2 or higher.

So continuing from the example we’ve been using in the previous posts above, let’s see how we can get our Web API to return results to a LightSwitch Silverlight client.

Modifying our Web API

By default, Web API will return our results in JSON format. This is a great, lightweight format for exchanging data and is standard across a plethora of platforms including our LightSwitch HTML client which is based on jQuery mobile. You can also return JSON to a Silverlight client. However you may want to work with XML instead. The nice thing about Web API is that it will return XML formatted results as long as the client specifies “application/xml” in its Accept header when making the web request. (As an aside, LightSwitch OData services will also return data in both these formats and the LightSwitch HTML & Silverlight clients use JSON under the hood.)

Let’s make a few modifications to the Get method in our Web API Controller so that it returns a list of objects we can serialize nicely as XML. First add a reference from the Server project to System.Runtime.Serialization and import the namespace in your Controller class.

image

Recall that our Get method calls a stored procedure in our database that returns a list of all the tables in the database as well as the number of rows in each. So open up the TableCountsController and create a class called TableInfo that represents this data. Then attribute the class with DataContract and DataMember attributes so it serializes how we need. (Please see MSDN for more information on DataContracts).

VB:

<DataContract(Namespace:="")>
Public Class TableInfo
    <DataMember>
    Property Name As String
    <DataMember>
    Property Count As Integer
End Class

C#

[DataContract(Namespace="")]
public class TableInfo
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]    
    public int Count { get; set; }
}

Now we can tweak the code (in bold) that calls our stored proc to return a List of TableInfo objects. Note that this change will not affect our JSON clients.

VB:

Public Class TableCountsController
    Inherits ApiController

    ' GET api/<controller>
    Public Function GetValues() As List(Of TableInfo)

        Dim reportResult As List(Of TableInfo) = Nothing

        Using context As ServerApplicationContext = ServerApplicationContext.CreateContext()

            'Only return this sensitive data if the logged in user has permission
            If context.Application.User.HasPermission(Permissions.SecurityAdministration) Then

                'The LightSwitch internal database connection string is stored in the 
                ' web.config as "_IntrinsicData". In order to get the name of external data 
                ' sources, use: context.DataWorkspace.*YourDataSourceName*.Details.Name
                Using conn As New SqlConnection(
                    ConfigurationManager.ConnectionStrings("_IntrinsicData").ConnectionString)

                    Dim cmd As New SqlCommand()
                    cmd.Connection = conn
                    cmd.CommandText = "uspGetTableCounts"
                    cmd.CommandType = CommandType.StoredProcedure
                    cmd.Connection.Open()
                    'Execute the reader into a new named type to be serialized
                    Using reader As SqlDataReader =
                        cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection)

                        reportResult = (From dr In reader.Cast(Of IDataRecord)()
                                        Select New TableInfo With {
                                            .Name = dr.GetString(0),
                                            .Count = dr.GetInt32(1)
                                        }
                                       ).ToList()

                    End Using
                End Using
            End If

            Return reportResult
        End Using
    End Function
End Class

C#:

public class TableCountsController : ApiController
{
    // GET api/<controller>
    public List<TableInfo> Get()
    {
        List<TableInfo> reportResult = null;
using (ServerApplicationContext context = ServerApplicationContext.CreateContext()) // Only return this sensitive data if the logged in user has permission if (context.Application.User.HasPermission(Permissions.SecurityAdministration)) { { //The LightSwitch internal database connection string is stored in the // web.config as "_IntrinsicData". In order to get the name of external data // sources, use: context.DataWorkspace.*YourDataSourceName*.Details.Name using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings ["_IntrinsicData"].ConnectionString)) { SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = "uspGetTableCounts"; cmd.CommandType = CommandType.StoredProcedure; cmd.Connection.Open(); // Execute the reader into a new named type to be serialized using (SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection)) { reportResult = reader.Cast<IDataRecord>() .Select(dr => new TableInfo { Name = dr.GetString(0), Count = dr.GetInt32(1) } ).ToList(); } } } } return reportResult; } }

Create a Custom Silverlight Control

LightSwitch let’s you write your own custom controls no matter what client you’re using. If you’re using the HTML client, you write custom JavaScript code, if you’re using the Silverlight client, you write XAML. So add a new Silverlight Class Library to your LightSwitch solution.

image

Then right-click on the Silverlight Class Library and Add –> New Item, and choose Silverlight User Control. Design your control how you wish using the XAML designer. For this example I’m keeping it simple. We will write the code to call the Web API and display the results in a simple DataGrid.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk=http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk 
x:Class="SilverlightClassLibrary1.SilverlightControl1" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <sdk:DataGrid Name="myGrid" IsReadOnly="True" AutoGenerateColumns="True" /> </Grid> </UserControl>

Calling Web API from Silverlight

Now we need to write the code for the control. With Silverlight, you can specify whether the browser or the client provides HTTP handling for your web requests. For this example we will use the browser via HttpWebRequest so that it will flow our credentials automatically with no fuss. (See How to: Specify Browser or Client HTTP Handling)

So you’ll need to call WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.BrowserHttp) when you instantiate the control.

We’ll also need to add a reference to System.Xml.Serialization and import that at the top of our control class. We’ll use the XMLSerializer to deserialize the response and populate our DataGrid. We use the HttpWebRequest to make the request. This is where we specify the path to our Web API based on the route we set up (which I showed that in the previous post).

(Note that the URI to the Web API is on the same domain as the client. The Web API is hosted in our LightSwitch server project which is part of the same solution as the desktop client. If you’re trying to use this code to do cross-domain access you’ll need to use a ClientAccessPolicy file to allow it. See Making a Service Available Across Domain Boundaries for more information.)

Here’s the complete user control code.

VB:

Imports System.Net
Imports System.Xml.Serialization
Imports System.IO

Public Class TableInfo
    Property Name As String
    Property Count As Integer
End Class
Partial Public Class SilverlightControl1
    Inherits UserControl

    Private TableCounts As List(Of TableInfo)

    Public Sub New()
        'Register BrowserHttp for these prefixes
        WebRequest.RegisterPrefix("http://",
                   System.Net.Browser.WebRequestCreator.BrowserHttp)
        WebRequest.RegisterPrefix("https://",
                   System.Net.Browser.WebRequestCreator.BrowserHttp)

        InitializeComponent()
        GetData()
    End Sub

    Private Sub GetData()
        'Construct the URI to our Web API
        Dim apiUri = New Uri(Application.Current.Host.Source, "/api/TableCounts/")
        'Make the request
        Dim request As HttpWebRequest = HttpWebRequest.Create(apiUri)
        request.Accept = "application/xml"
        request.BeginGetResponse(New AsyncCallback(AddressOf ProcessData), request)
    End Sub

    Private Sub ProcessData(ar As IAsyncResult)

        Dim request As HttpWebRequest = ar.AsyncState
        Dim response As HttpWebResponse = request.EndGetResponse(ar)

        'Deserialize the XML response
        Dim serializer As New XmlSerializer(GetType(List(Of TableInfo)))
        Using sr As New StreamReader(response.GetResponseStream(), 
System.Text.
Encoding.UTF8)
Me.TableCounts = serializer.Deserialize(sr) 'Display the data back on the UI thread Dispatcher.BeginInvoke(Sub() myGrid.ItemsSource = Me.TableCounts) End Using End Sub End Class

C#:

using System.Xml.Serialization;
using System.IO;

namespace SilverlightClassLibrary1
{
    public class TableInfo
    {
        public string Name { get; set; }
        public int Count { get; set; }
    }
    public partial class SilverlightControl1 : UserControl
    {

        private List<TableInfo> TableCounts;

        public SilverlightControl1()
        {
            //Register BrowserHttp for these prefixes 
            WebRequest.RegisterPrefix("http://",
                       System.Net.Browser.WebRequestCreator.BrowserHttp);
            WebRequest.RegisterPrefix("https://",
                       System.Net.Browser.WebRequestCreator.BrowserHttp);


            InitializeComponent();
            GetData();
        }

        private void GetData()
        {
             //Construct the URI to our Web API
            Uri apiUri = new Uri(Application.Current.Host.Source, "/api/TableCounts/");

            //Make the request
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(apiUri);
            request.Accept = "application/xml";
            request.BeginGetResponse(new AsyncCallback(ProcessData), request);
        }

        private void ProcessData(IAsyncResult ar)
        {
             HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar);

            //Deserialize the XML response
            XmlSerializer serializer = new XmlSerializer(typeof(List<TableInfo>));
            using (StreamReader sr = new StreamReader(response.GetResponseStream(), 
System.Text.
Encoding.UTF8)) { this.TableCounts = (List<TableInfo>)serializer.Deserialize(sr); //Display the data back on the UI thread
Dispatcher.BeginInvoke(() => myGrid.ItemsSource = this.TableCounts); } } } } 

Using the Custom Control in LightSwitch

Last but not least we need to add our custom control to a LightSwitch screen in our Silverlight client. Make sure to rebuild the solution first. Then add a new screen, select any screen template except New or Details, and don’t select any Screen Data.

image

In the screen content tree Add a new Custom Control.

image

Then add a Solution reference to your Silverlight class library and choose the custom control you built. (If you don’t see it show up, make sure you rebuild your solution.)

image

Finally, In the properties window I’ll name the control TableCounts, set the label position to “Top”, and set the Horizontal & Vertical alignment to “Stretch” so it looks good on the screen.

F5 to build and run the solution. (Make sure you are an administrator by checking “Grant for debug” on the access control tab for SecurityAdministration permission.) You should see the data returned from the stored procedure and displayed in the grid.

image

If you run the HTML client we built in the previous post, you’ll see that it runs the same as before with no changes. The JSON returned is the same shape as before.

image

Wrap Up

The last few posts I’ve taken you through some more advanced capabilities of LightSwitch. LightSwitch has a rich extensibility model which allows you to customize your LightSwitch applications beyond what’s in the box. When you need to provide custom functionality to your LightSwitch applications there are many options.

Using Web API with LightSwitch gives you the flexibility of creating custom web methods that can take advantage of all the data and business logic in your LightSwitch middle-tier via the ServerApplicationContext. If you have LightSwitch version 3 or higher (VS2012 Update 2+ or VS2013) then you are ready to get started.

For more information on using Web API with LightSwitch see:

And for more information on using database projects with LightSwitch in Visual Studio 2013 see:

Enjoy!

Leave a Comment
  • Please add 4 and 7 and type the answer here:
  • Post
  • Helpful

  • It seems as WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.BrowserHttp) on the control is always returning false, so that credentials are not passed.

  • It isn't clear above, but the Application object that the host is retrieved from is System.Windows.Application, not LightswitchApplication.Application.

Page 1 of 1 (3 items)