You are here:   Blog
Register   |  Login

LightSwitch News

 

Jul 1

Written by: Michael Washington
7/1/2012 9:23 PM  RssIcon

image

This article describes a proof of concept for a native mobile application that can run on an Android or IOS tablet, and communicate with a Visual Studio LightSwitch application using OData.

image

 

Fast Moving High-Performance Applications Are Native

image

When performing basic forms over data functions, HTML usually works sufficiently. However, when speed, and/or high fidelity graphics are required, an application that runs natively on the hardware device is needed. You can see in this article what can happen when you try to push HTML 5 too far.

image

I ran across a company that makes an impressive native IOS (IPad) application that allows restaurants to use an IPad running IOS to manage orders, but, it can be rather expensive for a small restaurant. I decided to see what it would take to create an application using relatively inexpensive tools such as Visual Studio LightSwitch and Unity3D (note, Unity is free to download and use, but it would cost $400 to enable deployment to IOS and Android).

While I am naturally comfortable creating the LightSwitch application, this is my first Unity3D project that I have ever done from scratch. The LightSwitch application took me less than an hour to complete, but the Unity3D part took dozens of hours. Because of this, I scaled back the proof of concept quite a bit.

I also demonstrate using a proxy page to reduce the amount of data being transmitted in and out of the LightSwitch application. While this example does not use security (to keep the amount of code on the Unity3D side down) you are able to use a proxy page and still enforce all LightSwitch security and business rules (see Communicating With LightSwitch Using Android App Inventor for an example of this).

The Application

image

The guests arrive at the restaurant and the host seats them. The host selects the table on her mobile tablet, and clicks the Create Seating button.

image

The waiter who's table it is, glances at their tablet and sees that the table cloth is on the table indicating that there is a seating, so they rush over to take the order.

image

The waiter takes the order. Chairs appear to indicate that the order has been taken.

image

The order shows up in the LightSwitch application, and the kitchen, running the LightSwitch Silverlight client, sees the order, and after preparing it, marks the meals ready.

image

The waiter knows the meals are ready when the cups appear.

When the guests leave the Complete Seating button is pressed to start the process from the beginning.

The LightSwitch Application

image

To keep the proof of concept as simple as possible I created a simple database structure where each seating can only have one or two possible meals. Of course in a real application you would use multiple related tables to allow unlimited orders composed of unlimited items. This is actually easy to do in LightSwitch, but time consuming to program on the Unity side.

However, I did add the following validation code to demonstrate how any business rules are enforced in all clients that connect to the application:

public partial class ApplicationDataService
{
    partial void Seatings_Validate(Seating entity, 
        EntitySetValidationResultsBuilder results)
    {
        if (entity.Id == 0)
        #region New Seating
        {
            // Check if there is an existing open Seating for this Table
            var ExitingOpenSeating = (from Seatings in DataWorkspace.ApplicationData.Seatings
                                        where Seatings.Table == entity.Table
                                        where Seatings.SeatingComplete == false
                                        select Seatings).FirstOrDefault();
            if (ExitingOpenSeating != null)
            {
                results.AddEntityError("Cannot add new Seating when one currently exists");
            }
        } 
        #endregion
        else 
        #region Existing Seating
        {
            // Get all changes
            EntityChangeSet changeSet = this.DataWorkspace.ApplicationData.Details.GetChanges();
            // Loop through all changes
            foreach (IEntityObject objEntity in changeSet.ModifiedEntities)
            {
                IEnumerable<Microsoft.LightSwitch.Details.IEntityProperty> changedProperties 
                    = ChangedProperties(objEntity);
                // Check if ChairOneMeal has been changed after it is Ready
                if (changedProperties.Any(p => p.Name.Equals("ChairOneMeal")))
                {
                    if (entity.ChairOneMealReady == true)
                    {
                        results.AddEntityError(
                            "Cannot change Chair One Meal after it is Ready.");
                    }
                }
                // Check if ChairTwoMeal has been changed after it is Ready
                if (changedProperties.Any(p => p.Name.Equals("ChairTwoMeal")))
                {
                    if (entity.ChairTwoMealReady == true)
                    {
                        results.AddEntityError(
                            "Cannot change Chair Two Meal after it is Ready.");
                    }
                }
                // Check if ChairOneMeal has been changed to Ready when it is null
                if (changedProperties.Any(p => p.Name.Equals("ChairOneMealReady")))
                {
                    if ((entity.ChairOneMeal == null) && (entity.ChairOneMealReady == true))
                    {
                        results.AddEntityError(
                            "Cannot change Chair One Meal to Ready if there is no Meal One");
                    }
                }
                // Check if ChairTwoMeal has been changed to Ready when it is null
                if (changedProperties.Any(p => p.Name.Equals("ChairTwoMealReady")))
                {
                    if ((entity.ChairTwoMeal == null) && (entity.ChairTwoMealReady == true))
                    {
                        results.AddEntityError(
                            "Cannot change Chair Two Meal to Ready if there is no Meal Two");
                    }
                }
            }
        } 
        #endregion
    }
    #region Extension Method
    // Code from Justin Anderson (Microsoft)
    // http://social.msdn.microsoft.com/Forums/en-US/lightswitch/thread/57665ea9-148c-42e5-8566-df85935caf53
    private static IEnumerable<Microsoft.LightSwitch.Details.IEntityProperty>
        ChangedProperties(IEntityObject entity)
    {
        return entity.Details.Properties.All()
            .OfType<Microsoft.LightSwitch.Details.IEntityTrackedProperty>()
            .Where(p => p.IsChanged);
    } 
    #endregion
}

 

When we cover the Unity client we will demonstrate how the rules are enforced.

The Proxy Page

Developing OData clients that communicate with LightSwitch is easy when there is an OData library available to assist you. You can find OData libraries at: http://www.odata.org/libraries.

image

However, OData can still be rather verbose.

image

Using a proxy page, we can reduce the amount of information greatly. However, we don’t have to sacrifice any of the benefits of OData. All business rules and security is still enforced.

image

To view the proxy page, we switch to File View in the LightSwitch project in Visual Studio.

image

The UnityProxy page is contained in the Server project. An OData service reference is required to make the page work, and the page must be added to the .lsproj file. The entire process to add server side OData code to LightSwitch is covered in: Calling LightSwitch 2011 OData Using Server Side Code (and the book: OData And Visual Studio LightSwitch).

image

The general outline of the code can be seen in the image above. Basically simple parameters are passed and the response is based on the Action parameter:

  • Meals – Each meal has a ID number that must be used when placing an order. This returns the meals and their ID number.
  • Status – This returns the status of all tables and their orders
  • New – Creates a new seating
  • Close – Closes the seating and allows a new one to be created
  • Order – sets or updates an order

For example, the code below creates a new seating (and returns the ID that will be used in subsequent calls). We have validation code in the LightSwitch application that will not allow a new seating if one already exists. However, in the code below we simply catch any validation error raised and pass it on to the client:

    if (Action == "new")
    {
        // Make a new seating
        try
        {
            LightSwitchApplication.LSRestaurantService.Seating objSeating =
                new LightSwitchApplication.LSRestaurantService.Seating();
            // Set values
            objSeating.ChairOneMealReady = false;
            objSeating.ChairTwoMealReady = false;
            objSeating.SeatingComplete = false;
            objSeating.SeatingTime = DateTime.Now;
            objSeating.Table = Convert.ToInt32(TableNumber);
            // Add new Seating
            objApplicationData.AddToSeatings(objSeating);
            objApplicationData.SaveChanges(System.Data.Services.Client.SaveChangesOptions.Batch);
            // Return SeatingID
            Response.Write(objSeating.Id);
        }
        catch (Exception ex)
        {
            Response.Write(ShowError(ex));
        }
    }

 

The Unity Application

image

In the Unity application, all the scripting code is contained in one C# file called ControlObjects that is attached to the Main Camera.

image

The script contains public properties.

image

These properties show up in the property panel for the script in the designer.

Elements are dragged and dropped on the properties in the designer to assign them and allow programmatic manipulation.

Unity GUI

Programming Unity takes a bit getting used to. Visual Studio is an event based environment. A person clicks on a button and you write code to respond to it. With Unity, everything runs in a constant loop.

To show UI elements, you write code in the GUI method. The GUI method, is a method that is run several times every second.

image

When we do not want a UI element to show we must wrap it in a condition statement that tests for a value in a variable that is used to handle state.

To draw things like a button to the screen, you use code such as the code below, that creates a box and a Create Seating button, and response if the button is clicked:

	#region Make Seating
	// Show Main Box 
	GUI.Box(new Rect (400,5,300,120), "");	
	
	// Only show the buton to make a Seating
	if (GUI.Button(new Rect(450, 35, 200, 50), "Create Seating"))   
	{
		// Set Seating
		SeatingComplete = false;
		// Show Tablecloth
		TableCloth.renderer.enabled = true;
		// Call LightSwitch and create the seating
		CreateSeating();
	}
	#endregion

 

When the Create Seating button is pressed the CreateSeating method runs:

 

	void CreateSeating() 
	{
	        // Contact the LightSwitch application and get the Meals
		string strURL = LightSwitchApplicationURL + "UnityProxy.aspx?Action=new&TableNumber=1";
		WWW www = new WWW(strURL);	
		
		// Call method to get all possible Meals
                StartCoroutine(CreateNewSeating(www));
	}

 

This calls the method that calls the LightSwitch proxy page:

 

	IEnumerator CreateNewSeating(WWW www)
	{
		// Return control for now but return
		// back to this method when the web call 
		// is complete        	
		yield return www;		
		
		// check for errors
    	        if (www.error == null)
    	        {   
			// Check for validation error from LightSwitch		
			int intErrorStartPosition = www.text.IndexOf(@"<ValidationResult>");
			
			if(intErrorStartPosition > 0)
			{
				// Get the inner exception message
				int intStartPosition = www.text.IndexOf(@"<Message>", intErrorStartPosition);
				int intEndPosition = www.text.IndexOf(@"</Message>", intErrorStartPosition);
				
				intStartPosition= intStartPosition + 9;
				
				// Set Error so it will display in the GUI method
				CurrentMessage = www.text.Substring(intStartPosition,(intEndPosition - (intStartPosition)));
				
				// Undo Set Seating
				SeatingComplete = true;
				// Show Tablecloth
				TableCloth.renderer.enabled = false;
			}
			else
			{
				// Clear Error display
				CurrentMessage = "";
				
				// Update Status to get the seatingID
				RefreshStatus(); 
			}
		}
		else 
		{
			// Show any errors
	                  print(www.error);
			// Set Error so it will display in the GUI method
			CurrentMessage = www.error;
                } 	
	}

 

image

If you try to create a seating when one already exists, the validation code will run in LightSwitch, pass through the proxy page, and display in the Unity application.

Note: You can use a plug-in such as IGUI that makes creating GUIs in Unity a lot easier.

A New Stack

Creating native applications provides the best performance. A huge disadvantage to creating native applications is the need to learn multiple languages. Using Unity, you can create a single application that can run on multiple devices. Using LightSwitch with Unity, allows you to easily create and maintain business logic.

Also See

Using Visual Studio LightSwitch To Orchestrate A Unity 3D Game

Download Code

The LightSwitch and Unity 3D source code is available at:

http://lightswitchhelpwebsite.com/Downloads.aspx

Tags: OData
Categories:

2 comment(s) so far...


Gravatar

Re: LightSwitch Restaurant Manager (OData and Unity3D)

I keep getting a Error Loading Type Library/DLL, I use unity3d a lot but do not develop a lot with visual studio, the error keeps coming up in Visual Studio, is there a library missing in the project?

Regards

Mario

By Mario Vermeulen on   7/4/2012 9:56 PM
Gravatar

Re: LightSwitch Restaurant Manager (OData and Unity3D)

@Mario Vermeulen - Make sure you are using Visual Studio 2012 RC

By Michael Washington on   7/4/2012 9:57 PM

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