You are here:   Blog
Register   |  Login

LightSwitch News

Mar 21

Written by: Michael Washington
3/21/2012 12:25 AM  RssIcon

image

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

image

When we log into the sample application, we see that it is a standard order entry application.

image

We have one administrator (Admin) and two non administrator users (JohnDoe, MaryDoe).

image

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.

image

A normal user will see only their orders.

image

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.

image

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).

image

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;
            }
        }

 

image

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

image

We now need to create a service reference to the LightSwitch OData service.

To do this, we switch to File View in Visual Studio.

image

We select Show All Files.

image

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).

image

The web browser will open and we will see a URL.

image

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).

image

We return to Visual Studio (keep the web browser open), and we right-click on Server/Service References and select Add Service Reference.

image

We paste in the URL that we copied in the Address box, and we click the Go button.

image

After the collections show, we change the namespace to ODataServiceReference and click OK.

image

If we try to build the project it may no longer build.

image

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

image

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;
            }
        }

 

image

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";
            }
        }
    }

 

image

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.

image

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.

image

We Right-click on the unloaded project and select Edit.

image

We add the entries for the pages to the _buildFile section.

image

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.

image

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...


Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Awesome!

By Jewel on   3/21/2012 5:51 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@ 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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Thanks Michael! This is exactly what I was looking for.

Cheers!!!

By Paul Patterson on   3/22/2012 5:19 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@Delordson Kallon / @Paul Patterson - Thank You!

By Michael Washington on   3/22/2012 5:24 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

** 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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Michael,
Great article

By Keith Craigo on   3/22/2012 5:40 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@Keith Craigo - Thank you for the support :)

By Michael Washington on   3/22/2012 5:40 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Nice work, Michael!

By Matt Sampson on   3/22/2012 6:52 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@Matt Sampson - Thanks Matt :)

By Michael Washington on   3/22/2012 6:53 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Michael,
Excellent walkthrough. A+

By PlusOrMinus on   3/22/2012 11:16 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@PlusOrMinus - Thank you, glad it helped.

By Michael Washington on   3/22/2012 11:48 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

Thank you Michael... Anyone converted this to vb.net? Thanks

By David Moss on   5/29/2012 6:45 AM
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

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
Gravatar

Re: Calling LightSwitch 2011 OData Using Server Side Code

@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

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation