OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Growth on The LightSwitchHelpWebsite.com

image

The best way to measure growth on the LightSwitchHelpWebsite.com is by looking at the number of members who join each month. Currently there are over 6000 members.

As you can see from the chart, growth was declining for years. I attribute this to the uncertainty around Silverlight. In the past, LightSwitch only created Silverlight applications.

This year, the Visual Studio LightSwitch team released the LightSwitch HTML Client.

As you can see from the chart, things have changed dramatically Smile


Understanding The LightSwitch HTML Client

image

The Microsoft Visual Studio LightSwitch team was a bit disappointed when I finally walked through the door. I was late to the invite only meeting where they revealed the beta of the new Visual Studio LightSwitch HTML Client. Yes, as long suspected, Visual Studio LightSwitch, the tool that previously only created Silverlight applications (Silverlight by this time was already facing huge challenges in its adoption because it did not run on any of the popular mobile devices), will now create HTML pages.

My flight from Los Angeles to Seattle was a bit late. I was so late in fact that the group was on their scheduled break. The team rolled back the slides and got me caught up. We get to the part about the HTML Client and I think the first words out of my mouth were, “Wow, that is great!” followed by “What about desktop?”.

The LightSwitch team is too nice to actually slap their palms to their foreheads in front of me and yell “Doh! he doesn’t get it!” but, really they should have. I didn’t get it. They calmly explained that they were “targeting mobile devices” because mobile is growing and creating applications that work well on mobile devices is really important. This may seem obvious now, however this conversation took place years ago.

What Is Different?

I have worked with the LightSwitch HTML Client for a few years now (today is was finally released). What I am only now understanding is that my desire to have it create a desktop HTML application was misguided. It’s like a person who only understands steam engines trying to understand a car (“That’s nice but it is too small to carry enough coal!”).

The main thing I needed to realize, was that if the primary purpose is to create the best HTML applications for the end user, their design is the best way to achieve that… whether you are using a mobile or a desktop HTML web browser.

The articles that you want to read are: A New User Experience (Heinrich Wendel) and: Designing for Multiple Form Factors (Heinrich Wendel).

image

This article shows how a LightSwitch application allows the end user to click on a screen element and quickly display a popup that allows them to view details or edit data. In a desktop application, the end-user will see the screen greyed out behind the popup. This allows the user to easily mentally track where they came from (and where they will return to).

image

If the user is using a tablet or a phone, the application will display only the popup (it will display the previous screen when they close the popup).

Also note that if a user is using a tablet, it will display the entire popup with both its columns, however, if the user is using a phone, it will display only one column at a time. The user will scroll down to get to all the information.

image

This is important, because we can create one application that works on all devices!

Single Page Application (because faster is better)

The Visual Studio LightSwitch HTML Client creates Single Page Applications. This design essentially means that when a end-user arrives at your LightSwitch application, a thin JavaScript framework loads and it communicates with the back-end services using OData.

Most importantly, you don’t have the slow postbacks that while they are merely annoying on a desktop application, make the same application practically unusable on a mobile device (because they move so slow due to being run on less powerful web browsers and slower internet connections). Instead of postbacks, only the data is transferred back and forth when a user views and saves data. The screen is not redrawn each time. This makes for a much faster application.

Desktop Apps Waste Space

image

Let’s look at an example of what I am talking about. I have a popular open source help desk ticket tracking application project called ADefHelpDesk. My plan is to make a new version using the LightSwitch HTML Client. My original thinking was to recreate the existing HTML screens. Now, it can be done, but I no longer believe it is a good idea and that is not what I plan to do.

Looking at the screen above, there are a lot of things on the page that do not need to be there. The reason they are there is that it takes so long for data to be displayed, I want to display as much information as possible. I also want all possible buttons and switches available without the need for the user to click-click-click to get to a needed button to perform a function.

The first thing LightSwitch does is move fast, very fast. Don’t take my word for it, see for yourself with the Live Demo: https://endtoendexample.lightswitchhelpwebsite.com/Client/default.htm (use your username and password from http://LightSwitchHelpWebsite.com).

So, from the beginning I need to remove anything that is not needed. If you want to search, click a button and do a search, otherwise hide all the search controls. When I do that 50% of all the items on the page go away!

image

When editing a help desk ticket, I have screen real estate used for things like the Description, Notes, Estimated Hours, Start and Compete boxes. The LightSwitch HTML Client is designed to allow a person to tap on an item and quickly edit it in a popup. Now I can eliminate the huge input boxes and show the data in much smaller text.

Changing The Look

image

Don’t worry, every LightSwitch application does not have to look like a LightSwitch app. We can use JavaScript plug-ins to create rich user interfaces.

image

LightSwitch also has a built-in Knockout-like JavaScript data binding framework that allows the user interface to update without requiring the end-user to explicitly save the page.

image

You can also use Themeroller to help you create new themes because LightSwitch uses standard .css style sheets.

Purpose Based Applications

An application has a purpose, a task that the end-user needs to accomplish. We want to create applications that allow a user to accomplish that task as fast and as efficiently as possible. The LightSwitch HTML Client creates applications that do that.


Forms Authentication with SignalR Using Web Forms and Windows Forms

image

A Method To Provide Some Security With SignalR

SignalR is a fantastic tool for creating real-time applications. It is not just limited to making chat applications, but we will use a chat application to demonstrate how you can implement Forms Authentication with SignalR.

The reason you would want to use Forms Authentication, is that most websites use Forms Authentication now. Forms Authentication also works with all web browsers and most devices.

SignalR

According to www.ASP.net:

SignalR is an open-source .NET library for building web applications that require live user interaction or real-time data updates. Examples include social applications, multiuser games, business collaboration, and news, weather, or financial update applications. These are often called real-time applications.

You can find more about SignalR at this link: http://signalr.net/.

The Sample Application

image

When we run the LightBulbSignalRService project we see a simple SignalR application that uses Persistent Connections (rather than the Hubs that most examples use).

When the application starts, we are not logged in so our connection GUID shows with a message that we are connected.

image

If we click the Login as TestUser button, we are given a cookie and SignalR detects that we are authenticated.

SignalR also puts us in a group that has the same name as our user name.

image

We can open up another Tab, log in again as TestUser, enter a message and click the broadcast button…

image

When we return to the first Tab, it will see the Group Message because it is in the same group. This is just to demonstrate that you can authenticate a user and keep data in sync across multiple instances,  and as will be demonstrated in the next step, multiple devices.

For example, imagine a user is working with your application on a web page and then wants to pick up from where they left off on their tablet or cell phone (or any other device).

image

Now, we run the LightBulbSignalRClient project and set the connection and click the Connect button.

image

We are now logged in via a Windows Forms Application using Forms Authentication!

The Web Application

image

We will first cover the Web Application (the LightBulbSignalRService project).

The first place we want to start is the web.config file. It is there that we want to turn Forms Authentication on by setting the authentication tag to mode=”Forms”:

xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms"/>
  system.web>
configuration>

image

Next, we use NuGet to pull in SignalR.

We create a Global.asax file and add the following code to the Application_Start:

image

This simply indicates that we will have a SignalR Persistent Connection called MyConnection that can be accessed by the end point “echo”.

To implement the MyConnection class, we use the following code in the MyConnection.cs file:

 

using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hosting;
public class MyConnection : PersistentConnection
{
    protected override Task OnReceived(IRequest request, string connectionId, string data)
    {
        // Broadcast to all instances of the user 
        // (that user is in a group that matches their user name)
        Groups.Send(getClientDescription(request, connectionId), "Group Message: " + data);
        // Broadcast to all clients
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " Sent: " + data);
    }
    protected override Task OnConnected(IRequest request, string connectionId)
    {
        // Add the user to a group that matches their user name
        // If they are not logged in, their group will be their
        // Connection ID
        Groups.Add(connectionId, getClientDescription(request, connectionId));
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " connected");
    }
    protected override Task OnReconnected(IRequest request, string connectionId)
    {
        // User Reconnected (they come back)
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " re-connected");
    }
    protected override Task OnDisconnected(IRequest request, string connectionId)
    {
        // User Disconnected (they may come back)
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " disconnected");
    }
    // Utility
    private static string getClientDescription(IRequest request, string connectionId)
    {
        // Note: Using HttpContext.Current is a no no with SignalR, 
        // so get the IPrincipal from the SignalR IRequest via IRequest.User
        IPrincipal objIPrincipal = request.User;
        if (objIPrincipal != null)
        {
            // If user is authenticated get user name
            // otherwise use connectionId
            var name = objIPrincipal.Identity.IsAuthenticated
                    ? objIPrincipal.Identity.Name
                    : connectionId;
            return name;
        }
        else
        {
            return connectionId;
        }
    } 
}

image

Next, we create a Default.aspx page.

 

It uses the following markup:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>SignalR Testtitle>
    <script src="Scripts/jquery-1.9.1.min.js" type="text/javascript">script>
    <script src="Scripts/json2.js" type="text/javascript">script>
    <script src="Scripts/jquery.signalR-1.0.0.min.js" type="text/javascript">script>
    <script type="text/javascript">
        $(function () {
            // For IIS deployment use: $.connection('echo');
            var connection = $.connection('/echo');
            // This is fired when a message is received
            connection.received(function (data) {
                $('#messages').append('<li>' + data + 'li>');
            });
            // Start the connection
            connection.start().done(function () {
                $("#broadcast").click(function () {
                    connection.send($('#msg').val());
                });
            });
        });
    script>
head>
<body>
    <form id="form1" runat="server">
        <div>
            <input type="text" id="msg" />
            <input type="button" id="broadcast" value="broadcast" /> 
            <asp:Button ID="btnLoginAsTest" runat="server" 
                OnClick="btnLoginAsTest_Click" Text="Login as TestUser" />
             
            <asp:Button ID="btnLogout" runat="server" 
                OnClick="btnLogout_Click" Text="Logout" />
             <ul id="messages">
            ul>
        div>
    form>
body>
html>

 

And the following code behind:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace LightBulbSignalRService
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        #region btnLoginAsTest_Click
        protected void btnLoginAsTest_Click(object sender, EventArgs e)
        {
            // *************************
            // Insert user validation here
            // For example, check the username and password in the database
            // *************************
            // Log the user into the site
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
              "TestUser",
              DateTime.Now,
              DateTime.Now.AddDays(30),
              false,
              "SignalR",
              FormsAuthentication.FormsCookiePath);
            // Encrypt the ticket.
            string encTicket = FormsAuthentication.Encrypt(ticket);
            // Create the cookie.
            Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
        } 
        #endregion
        #region btnLogout_Click
        protected void btnLogout_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
        } 
        #endregion
    }
}

 

To authenticate remote users, we create a file CreateCookie.aspx with the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
namespace ODataSample
{
    public partial class CreateCookie : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string strUserName = Request.QueryString["UserName"];
            string strPassword = Request.QueryString["Password"];
            // *************************
            // Insert user validation here
            // For example, check the username and password in the database
            // *************************
            // Create cookie and return it
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                strUserName,
                DateTime.Now,
                DateTime.Now.AddDays(30),
                false,
                "SignalR",
                FormsAuthentication.FormsCookiePath);
            // Encrypt the ticket.
            string encTicket = FormsAuthentication.Encrypt(ticket);
            // Create the cookie.
            Response.Write(encTicket);
            Response.End();
        }
    }
}

The Windows Forms Client

image

The Windows Forms Client has to have the ASP.NET SignalR Client installed.

image

We use NuGet to pull in the .NET SignalR Client.

We use the following code for the Connect button:

            // Connect to the service
            connection = new Connection(txtSignalRWebsite.Text);
            // Fire connection_Received when message comes in
            connection.Received += connection_Received;
            // Create a Cookie
            Cookie objCookie = new Cookie();
            // Set the values
            objCookie.Domain = txtDomain.Text;
            objCookie.Expires = DateTime.Now.AddMinutes(20);
            objCookie.HttpOnly = false;
            objCookie.Name = ".ASPXAUTH";
            objCookie.Path = @"/";
            objCookie.Secure = false;
            objCookie.Value = GetCookie(); // The Forms Auth Ticket
            // Create a Cookie container and put the Cookie inside
            connection.CookieContainer = new CookieContainer();
            connection.CookieContainer.Add(objCookie);
            // Start the connection
            connection.Start().Wait();

 

The following is the code that connects to the CreateCookie.aspx file and gets the cookie:

 

        private string GetCookie()
        {
            WebClient client = new WebClient();
            // Add a user agent header in case the 
            // requested URI contains a query.
            client.Headers.Add("user-agent", 
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
            
            string strWebRequest = 
                String.Format("{0}?UserName={1}&Password={2}", 
                txtAuthWebsite.Text, txtUserName.Text, txtPassword.Text);
            Stream data = client.OpenRead(strWebRequest);
            StreamReader reader = new StreamReader(data);
            string strCookie = reader.ReadToEnd();
            data.Close();
            reader.Close();
            return strCookie;
        } 

 

SignalR Authentication

image

This method was created because this is what it appears that one of the SignalR creators, David Fowler indicated was the way it should be handled (http://stackoverflow.com/questions/11488461/forms-authentication-with-signalr).

Download Sample Code

You can download the code from this link:

http://silverlight.adefwebserver.com/Files/SignalrRWindowsFormsAuth.zip


My 8 Year Old Cousin Creates A Computer Game

image

My 8 year old cousin just completed his first Android game today. It is called Pet Brawl. He actually wrote it himself. I have been tutoring him for 1 1/2 hours each month for over a year, but he wrote all the code himself.

image

Originally I started working with him after he had completed a computer camp where they taught him how to use the computer programming application called Scratch. It is a great program and I recommend it highly as an entry level programming language for all ages.

Our sessions would start with a task I would assign him at the end of the previous session. Things like “make the cat pick up four objects in order”. They were designed to be too hard and when we got together I would explain how to solve the problem.

image

Next we moved to App Inventor. Another programming language and application builder created by some of the same people that created Scratch. The nice thing about App Inventor is that you can run the programs you create on your Android device.

image

This time I set an ambitious goal. He would complete an entire program and we would distribute it. Originally he wanted the game to have you to pick a “pet” and then you would “brawl” against the computer.

First we started by learning how to move the character around the screen (I had to figure it out myself and I led the Google session, and after finding an example I would provide guidance and encouragement and he worked it out). I only touched the keyboard less than 2 minutes a session (usually to find the element I wanted him to try next).

As time went on he scaled the game back a bit (he actually wanted to finish it), so the final game just has you trying to survive (don’t let the Phoenix touch you, the Dragon) until the clock runs down.

image

The game is actually quite challenging. He programmed it himself, and he is only eight years old.

image

The picture above is Carter showing his father the game he just created running on his father’s Galaxy Note.

You can download the .apk at the link below. It will run on most any Android device.

Download: PetBrall.apk


Calling OData From A Normal ASP.NET Page

 

image

OData is basically like web services but better because you do not have to keep updating your service end points. For example, if you expose a Customer entity (table) as a web service, you have to create a service end point such as GetCustomer(int CustomerID). When the consumers of your web service want to search for Customers by last name you have to add a new service end point.

With OData you only need to expose the Customer entity and practically all possible ways the consumers need to access the data is automatically taken care of.

Recently at work I advocated exposing a collection of data that the enterprise needed as OData. In this case I would be a consumer and I really wanted the flexibility OData provides. The question was asked as to how to secure it? I showed the code from this article: Simple Example To Secure WCF Data Service OData Methods. The problem with that example is that the consumer is a Silverlight control and most of the consumers are using standard ASP.NET web pages. I then showed them this article: A Full CRUD LightSwitch JQuery Mobile Application. The problem with that article is that it is inside LightSwitch and I am the only one using LightSwitch (so far Smile).

I looked for a simple example showing how to call OData from a normal ASP.NET page and could not find one, so I decided to make one.

The Service

image

We start with the service. You can download the code at the end of this blog, and you can play with it live at: http://odatasample.adefwebserver.com/. To understand how it works see the article: Simple Example To Secure WCF Data Service OData Methods.

When you use the dropdown to change users, a Forms Authentication cookie is created for the web browser. The Silverlight client automatically uses that cookie when making requests.

It you close the web browser and try to go directly to the service: http://odatasample.adefwebserver.com/Service.svc/SampleCustomerData you wont get any data:

image

If you go to the main page and use the dropdown to log in and then hit the service again, you will see data:

image

The code to protect the service is simple code that checks to see if you are logged in and what your username is and filters the data to only show the data that matches your user name:

    public IQueryable SampleCustomerData
    {
        get
        {
            // Ensure user is authenticated
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                // Get the current user
                string strUser = 
                    Convert.ToString(HttpContext.Current.User.Identity.Name);
                // Filter results
                var result =
                    _sampleCustomerRecordList.Where(x => x.CustomerID == strUser).ToList();
                // Return results
                return result.AsQueryable();
            }
            else
            {
                // Not Authenticated
                var result = new List();
                return result.AsQueryable();
            }
        }
    }

 

The difference between seeing data and not seeing data is based on what is returned by HttpContext.Current.User.Identity.Name and that is determined by the Forms authentication cookie.

Create A Forms Auth Ticket So You Can Create A Cookie

image

I added a page to the project called CreateCookie.aspx using the following code:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (
            (Request.QueryString["UserName"] == "UserOne") 
            && (Request.QueryString["Password"] == "password")
            )
        {
            // Create cookie and return it
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                "UserOne",
                DateTime.Now,
                DateTime.Now.AddDays(30),
                false,
                "Role One",
                FormsAuthentication.FormsCookiePath);
            // Encrypt the ticket.
            string encTicket = FormsAuthentication.Encrypt(ticket);
            // Create the cookie.
            Response.Write(encTicket);
            Response.End();
        }
    }

 

When you call the page with the proper username and password it returns the Forms Auth Ticket that you can use to create a valid cookie.

The Client

image

Now let’s look at the sample client code.

The first thing we do is add a service reference to the OData service at:

http://odatasample.adefwebserver.com/Service.svc (you cannot browse only to the .svc file in your web browser, but you can add a service reference to it)

When we click on the Get Cookie button we get a Forms Auth Ticket (cookie) using this code:

    protected void btnGetCookie_Click(object sender, EventArgs e)
    {
        string strBaseURL = String.Format(@"{0}/{1}", 
            txtODataServiceURL.Text, 
                @"CreateCookie.aspx?UserName=UserOne&Password=password");
        HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(strBaseURL);
        myRequest.Method = "GET";
        WebResponse myResponse = myRequest.GetResponse();
        StreamReader sr = 
            new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8);
        string result = sr.ReadToEnd();
        sr.Close();
        myResponse.Close();
        lblCookie.Text = String.Format("{0}", result);
    }

image

Once we have the Forms Auth Ticket, we can call the OData service using the following code:

    protected void btnCallOData_Click(object sender, EventArgs e)
    {
        string strBaseURL = txtODataServiceURL.Text;
        // Create DataContext
        ODataServiceReference.SampleDataSource objSampleDataSource =
            new ODataServiceReference.SampleDataSource(
                new Uri(string.Format(@"{0}/Service.svc/", strBaseURL)));
        // An event handler that will run when the call is being sent
        // We do this so we can add the cookie
        objSampleDataSource.SendingRequest += objSampleDataSource_SendingRequest;
        // Query OData source
        var result = from CustomerData in objSampleDataSource.SampleCustomerData
                     select CustomerData;
        // Bind the data to te DataGrid
        gvDataGrid.DataSource = result;
        gvDataGrid.DataBind();
    }
    void objSampleDataSource_SendingRequest(object sender, 
        System.Data.Services.Client.SendingRequestEventArgs e)
    {
        // Create a Cookie
        Cookie objCookie = new Cookie();
        // Set the values
        objCookie.Domain = txtODataServiceURL.Text.Replace(@"http://", "");
        objCookie.Expires = DateTime.Now.AddMinutes(20);
        objCookie.HttpOnly = false;
        objCookie.Name = ".ASPXAUTH";
        objCookie.Path = @"/";
        objCookie.Secure = false;
        objCookie.Value = lblCookie.Text; // The Forms Auth Ticket
        // Create a Cookie container and put the Cookie inside
        ((HttpWebRequest)e.Request).CookieContainer = new CookieContainer();
        ((HttpWebRequest)e.Request).CookieContainer.Add(objCookie);
    }

More Information

OData and Authentication

Download Code

ODataClientNew.zip

ODataSampleNew.zip