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:
- Adding Stored Procs to your LightSwitch Intrinsic Database
- Calling Stored Procs in your LightSwitch Databases using Web API
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.
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
[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 TableCountsControllerInherits ApiController' GET api/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 permissionIf 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.NameUsing 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 serializedUsing 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 reportResultEnd Using End Function End Class
C#:
public class TableCountsController : ApiController{// GET api/public List<TableInfo>Get() {List<TableInfo> reportResult = null;
using (ServerApplicationContext context = ServerApplicationContext.CreateContext())// Only return this sensitive data if the logged in user has permissionif (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.Nameusing (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 serializedusing (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.
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.
<UserControlxmlns="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.NetImports System.Xml.SerializationImports System.IOPublic Class TableInfoProperty Name As String Property Count As Integer End Class Partial Public Class SilverlightControl1Inherits UserControlPrivate TableCounts As List(Of TableInfo)Public Sub New()'Register BrowserHttp for these prefixesWebRequest.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 APIDim apiUri = New Uri(Application.Current.Host.Source, "/api/TableCounts/")'Make the requestDim 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.AsyncStateDim response As HttpWebResponse = request.EndGetResponse(ar)'Deserialize the XML responseDim 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 threadDispatcher.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 APIUri apiUri = new Uri(Application.Current.Host.Source, "/api/TableCounts/");//Make the requestHttpWebRequest 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 responseXmlSerializer 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.
In the screen content tree Add a new Custom Control.
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.)
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.
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.
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:
- Using the LightSwitch ServerApplicationContext API
- A New API for LightSwitch Server Interaction: The ServerApplicationContext
- Create Dashboard Reports with LightSwitch, WebAPI and ServerApplicationContext
- Dashboard Reports with LightSwitch, WebAPI and ServerApplicationContext– Part Deux
And for more information on using database projects with LightSwitch in Visual Studio 2013 see:
Enjoy!