Mar
21
Written by:
Michael Washington
3/21/2012 12:25 AM
Visual Studio LightSwitch 2011 (In Visual Studio 11) allows you to access your LightSwitch application via OData. This allows you to access the business layer and security of your LightSwitch application. It also allows your LightSwitch application to communicate with web pages, mobile clients (IPad, Android, Windows Phone) and other clients such as Excel PowerPivot.
In this article we will cover creating ASP.NET .aspx pages to connect to LightSwitch using OData. This is different than the article: Integrating Visual Studio LightSwitch Application Into An Existing Website using IFrames because in that article we only connected to the database that LightSwitch is using. We are not able to access the business logic and security of the LightSwitch application.
There are many ways to call LightSwitch OData collections. This article will demonstrate methods that require only server side code, no JavaScript.
The Application
When we log into the sample application, we see that it is a standard order entry application.
We have one administrator (Admin) and two non administrator users (JohnDoe, MaryDoe).
If we navigate to the Login.aspx page, we see a web application that is optimized for mobile displays.
We log in using the normal LightSwitch username and password for each account.
A normal user will see only their orders.
An Administrator will see orders for all users.
Set-Up – Security
When we expose our LightSwitch application entities over OData, we must set security. This is not hard because we have to create security for the application in the process of developing our application anyway.
For example, to set security on the Order entity, we select the Write Code menu, and then the _Filter method (this is basically LightSwitch’s version of a Query Interceptor).
This takes us to the code page where we can add filters such as the following that allow a user to only see their own Orders, but an Administrator to see all Orders:
partial void FlowerShopOrders_Filter(ref Expression<Func<FlowerShopOrder, bool>> filter)
{
if(!this.Application.User.HasPermission(Permissions.SecurityAdministration))
{
filter = e => e.FlowerShopCustomer.Username == this.Application.User.Name;
}
}
We also go into Settings and ensure we have Forms authentication enabled.
(you can use other authentication methods, but the code in this example will only work with Forms authentication)
Set-Up – OData Reference
We now need to create a service reference to the LightSwitch OData service.
To do this, we switch to File View in Visual Studio.
We select Show All Files.
Next, we Start Without Debugging (you will want to have a LightSwitch screen, or entity, opened in design mode for Visual Studio to open the application properly).
The web browser will open and we will see a URL.
We alter the URL to point the URL to applicationData.svc, and copy the entire URL (you may have to turn off Feed Reading view in Internet Explorer to see the collections in the web browser).
We return to Visual Studio (keep the web browser open), and we right-click on Server/Service References and select Add Service Reference.
We paste in the URL that we copied in the Address box, and we click the Go button.
After the collections show, we change the namespace to ODataServiceReference and click OK.
If we try to build the project it may no longer build.
We have to remove the System.Data.Services.Client assembly reference from the Server project to fix the problem.
Set-Up – LightSwitch OData Forms Authentication Security
Now, we follow the Forms Authentication Configuration directions in the article: Securing OData Services using Forms Authentication.
We add a partial class that will get the authentication cookie that we create when the user logs in, and then we use it to set an authentication cookie for each OData call:
partial void OnContextCreated()
{
this.SendingRequest +=
new EventHandler<SendingRequestEventArgs>(OnSendingRequest);
}
void OnSendingRequest(object sender, SendingRequestEventArgs e)
{
// Get the Forms auth cookie
var AuthCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (AuthCookie != null)
{
Cookie objCookie = new Cookie();
objCookie.Domain = HttpContext.Current.Request.Url.DnsSafeHost;
objCookie.Expires = AuthCookie.Expires;
objCookie.HttpOnly = AuthCookie.HttpOnly;
objCookie.Name = AuthCookie.Name;
objCookie.Path = AuthCookie.Path;
objCookie.Secure = AuthCookie.Secure;
objCookie.Value = AuthCookie.Value;
((HttpWebRequest)e.Request).CookieContainer = new CookieContainer();
((HttpWebRequest)e.Request).CookieContainer.Add(objCookie);
}
}
The following code is used to determine the current base address of the LightSwitch application so that the URL path to the OData service will be dynamically set:
public static string BaseSiteUrl
{
get
{
HttpContext context = HttpContext.Current;
string baseUrl =
context.Request.Url.Scheme
+ "://"
+ context.Request.Url.Authority
+ context.Request.ApplicationPath.TrimEnd('/') + '/';
return baseUrl;
}
}
We also add a Login.aspx and a MobileMenu.aspx page.
In the Login page, we use the following code:
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// When a user first comes to this page log them out
if (!Page.IsPostBack)
{
FormsAuthentication.SignOut();
}
}
protected void ctrlLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
if (Membership.ValidateUser(ctrlLogin.UserName, ctrlLogin.Password))
{
// Set the forms authentication cookie that will be used in the OData requests
FormsAuthentication.SetAuthCookie(ctrlLogin.UserName, false);
e.Authenticated = true;
Response.Redirect("~/MobileMenu.aspx");
}
else
{
e.Authenticated = false;
lblMessage.Text = "Login failed";
}
}
}
We use the following code for the markup of the MobileMenu.aspx page:
<div data-role="page">
<div data-role="header" align="center" data-theme="c">
Mobile Menu
</div>
<!-- /header -->
<div data-role="content">
<asp:gridview id="gvOrders" runat="server" autogeneratecolumns="False">
<Columns>
<asp:TemplateField HeaderText=" ">
<ItemTemplate>
<table align="center" border="1">
<tr>
<td><strong>Order Name:</strong></td>
<td>
<asp:Label runat="server" Text='<%# Eval("OrderName") %>' />
</td>
</tr>
<tr>
<td>
<strong>Order Date:</strong></td>
<td>
<asp:Label runat="server" Text='<%# Eval("OrderDate") %>' />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center">
<asp:GridView ID="gvOrderDetails" runat="server" autogeneratecolumns="False"
DataSource='<%# Eval("OrderDetailsInfo") %>' align="center" Width="80%">
<Columns>
<asp:BoundField DataField="Product" HeaderText="Product" />
<asp:BoundField DataField="Quantity" HeaderText="Quantity">
<ItemStyle HorizontalAlign="Center" />
</asp:BoundField>
</Columns>
</asp:GridView>
</td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:gridview>
<br />
<asp:hyperlink id="lnkLogOff" runat="server" navigateurl="~/Login.aspx" data-role="button"
data-ajax="false" data-icon="star" data-theme="b" text="Log Off" />
</div>
The following code is used to display the Orders:
private void ShowOrders()
{
// Create DataContext
ODataServiceReference.ApplicationData objApplicationData =
new ODataServiceReference.ApplicationData(new Uri(string.Format(@"{0}applicationdata.svc/", BaseSiteUrl)));
// Query OData source
var result = from FlowerShopOrders in objApplicationData.FlowerShopOrders
.Expand(x => x.FlowerShopCustomer)
.Expand(x => x.FlowerShopOrderDetail)
select FlowerShopOrders;
// Collection to hold Orders
List<OrderInfo> colOrderInfo = new List<OrderInfo>();
// Loop thru orders
foreach (var item in result)
{
// Create new Order
OrderInfo objOrderInfo = new OrderInfo();
objOrderInfo.OrderID = item.Id;
objOrderInfo.OrderName =
String.Format("[{0}] {1}, {2} - {3}",
item.Id,
item.FlowerShopCustomer.LastName,
item.FlowerShopCustomer.FirstName,
item.OrderDate.ToShortDateString());
objOrderInfo.OrderDate = item.OrderDate;
// Collection to hold Order Details
objOrderInfo.OrderDetailsInfo = new List<OrderDetailsInfo>();
// Loop thru order details
foreach (var OrderDetail in item.FlowerShopOrderDetail)
{
// Create new Order Detail
OrderDetailsInfo objOrderDetailsInfo = new OrderDetailsInfo();
// Create a new DataContext
ODataServiceReference.ApplicationData objApplicationData2 =
new ODataServiceReference.ApplicationData(new Uri(string.Format(@"{0}applicationdata.svc/", BaseSiteUrl)));
// Query OData source
var objOrderDetails = (from OrderDetails in objApplicationData2.FlowerShopOrderDetails
.Expand(x => x.FlowerShopProduct)
where OrderDetails.Id == OrderDetail.Id
select OrderDetails).FirstOrDefault();
objOrderDetailsInfo.Id = OrderDetail.Id;
objOrderDetailsInfo.Product = objOrderDetails.FlowerShopProduct.ProductName;
objOrderDetailsInfo.Quantity = OrderDetail.Quantity;
// Add Order Detail
objOrderInfo.OrderDetailsInfo.Add(objOrderDetailsInfo);
}
// Add Order
colOrderInfo.Add(objOrderInfo);
}
// Bind entire colection to GridView
gvOrders.DataSource = colOrderInfo;
gvOrders.DataBind();
}
Adding The .ASPX Pages To The Build
We now need to add the Login.aspx and a MobileMenu.aspx pages to the build file, so that LightSwitch will include them in the build.
If we do not do this, the pages will not be present when you debug or Publish the application.
We switch to File View, right-click on the LightSwitch project, and select Unload Project.
Note: If Visual Studio throws an error or crashes, just close Visual Studio and then re-open the project.
We Right-click on the unloaded project and select Edit.
We add the entries for the pages to the _buildFile section.
We then Reload the project.
Debugging Using ‘Test User’
When you are debugging with LightSwitch, you are always a user named TestUser. You are also automatically logged in.
To log in as this user in the Login.aspx page, you will need the password. To establish a password, you will need to add a user named TestUser to the Users table in LightSwitch security. The password that you set is the password that you will enter into the Login.aspx page.
An Important Note About SSL
All the OData calls in this application contain the username and password sent in clear text. This is because this sample application is using Forms Security and the ‘client’ is a standard web page.
A production application must run on a webserver that has SSL enabled for all transactions. Otherwise, a hacker with a packet sniffer can easily get the usernames and passwords of your users who are connecting to your site using public Wi-Fi access points or other unsecure networks.
Also See
Learn How To Make OData Calls In LightSwitch 2011
Accessing Data Service Resources (WCF Data Services)
OData: Operations
Calling Service Operations (WCF Data Services)
Does OData Data Source Connection Support JSON Format?
OData and Authentication – Part 7 – Forms Authentication
Special Thanks
I would like to thank the entire LightSwitch team for all their help is putting this article together. Matt Thalman and Eric Erhardt provided critical feedback and direction that allowed me to remove most of the complex steps and code.
Download Code
The LightSwitch project is available at:
http://lightswitchhelpwebsite.com/Downloads.aspx
18 comment(s) so far...
Awesome!
By Jewel on
3/21/2012 5:51 AM
|
@ Jewel - Thanks! Also, I am working with Microsoft to try and find a way to remove the username password and just use a Form Auth Cookie. Check back next week because if I can get that change to work I should have this article updated by then.
By Michael Washington on
3/21/2012 7:43 AM
|
Wow. This is exactly what we've all been waiting for. A concrete example of how to use LightSwitch OData for html pages. This is great!
By Delordson Kallon on
3/21/2012 9:25 AM
|
Thanks Michael! This is exactly what I was looking for.
Cheers!!!
By Paul Patterson on
3/22/2012 5:19 AM
|
@Delordson Kallon / @Paul Patterson - Thank You!
By Michael Washington on
3/22/2012 5:24 AM
|
** UPDATE **
This article has been updated and the source code updated after Microsoft provided guidance and improvements.
By Michael Washington on
3/22/2012 5:25 AM
|
Michael, Great article
By Keith Craigo on
3/22/2012 5:40 AM
|
@Keith Craigo - Thank you for the support :)
By Michael Washington on
3/22/2012 5:40 AM
|
Nice work, Michael!
By Matt Sampson on
3/22/2012 6:52 AM
|
@Matt Sampson - Thanks Matt :)
By Michael Washington on
3/22/2012 6:53 AM
|
Michael, Excellent walkthrough. A+
By PlusOrMinus on
3/22/2012 11:16 AM
|
@PlusOrMinus - Thank you, glad it helped.
By Michael Washington on
3/22/2012 11:48 AM
|
Thank you Michael... Anyone converted this to vb.net? Thanks
By David Moss on
5/29/2012 6:45 AM
|
Hey Michael,
None of the filtering appears to be working for me.... using VS11 lightswitch no matter who logs in all the orders show? It is also the case with the CRUD OData example? Thanks for any direction.
By David Moss on
6/6/2012 9:09 AM
|
@David Moss - You need to publish the application to really test it. When you are debugging and you have "Security Administration" 'granted for debug' checked you will always be an administrator and see all the orders. Uncheck that and you will only see the orders for the Test User.
By Michael Washington on
6/6/2012 9:22 PM
|
Hi Michael, Thanks for this. Have you thought about doing a simple service (hello world style) that a standard android app (or any programmatic application) could consume? This would be really helpful as it would take out ASP and other non Lightswitch elements (e.g. ASAP).
By anton on
7/17/2012 3:56 AM
|
Thanks Michael. Great help for me.
BTW, Where is the "OnContextCreated" to put in? I hardly find the right place in my LightSwitch 2012 project. Can you help me?
By YJ on
10/17/2012 4:18 AM
|
@YJ - You will want to download the sample code and upgrade it to LightSwitch 2012.
By Michael Washington on
10/17/2012 4:19 AM
|