Note: I’m learning about Azure and Mobile Web App development as I go along. If you see any mistakes or better ways of doing things please drop me an email : nick@lightningtools.com
With more people becoming mobile workers, or working from home – business data that is stored away in on premise databases needs to be set free for people to use wherever they are, on whatever device they like. Accomplishing this can be a challenge for any size organization, opening firewall ports, active directory federated services, WCF service endpoints etc…
Setting the data free
If you have an on premise data source that you want to make available to mobile workers you do have a number of options:
1, SharePoint 2010, BCS + SharePoint Workspace
This is good for users who may not always have Internet connectivity as they can work offline and resync
2, Present the data to the outside world through a WCF service hosted on premise.
This does mean exposing parts of the network to the outside world and needing to manage authentication to the WCF service. Certainly not impossible, but it does present challenges and risks.
3, Using bits and pieces now available in Windows Azure
Using the Service Bus, we can expose the data through the firewall without needing to open up any extra ports. The authentication and security is managed through Azure Service Bus and ACS.
I’m presuming we are working with a business that does not want to configure and setup ADFSv2. With many companies these days allowing employees to buy and use their own mobile or tablet devices managing these internally becomes a challenge.
The actual data presentation solution will be an ASP.NET MVC web application running on Windows Azure. We’ll use the jQueryMobile javascript library to present the data in a nice mobile looking way. This saves us having to build separate mobile applications for iPhone, Windows Phone, Android etc… Also having it as a web application allows us to easily rollout changes rather than expecting users to manually upgrade a native application.
We’ll use a simple example. In our company we have a product stock control system, and our people on the road need to be able to check the stock levels before placing an order. Working with a simple Products table:
ProductId – int, primary key
Name – varchar(256)
Price – money
StockLevel – int
We will use the Azure Service Bus to allow on premise data to be made available to the outside world. The Service Bus has 2 endpoints. The Listener endpoints will sit inside the organization, constantly running and listening for calls to execute methods that return data. The second component is a caller application, that calls the Listener methods over the service bus. The service bus makes sure the connection between the caller and listener are secure.
Assumption : this walk through assumes you already have an Azure account up and running, with the necessary Azure SDK’s and tools installed. To get an account setup and download the tools you need visit www.azure.com
Configure the Service Bus
1, Log in to www.azure.com and click the Manage button to open up the management portal.
2, Once in the management portal, click on the ‘Service Bus, Access Control & Caching’ tab
3, Click on the ‘New’ button within the ‘Service Namespace’ section
4, Give the new service bus you want to create a name, such as MobileProducts and select a location for the ServiceBus to run that makes sense geographically
5, Once your Service Bus is successfully created, click on the MobileProducts name in the list of buses available. This will display a list of properties down the right side.
6, Click on the ‘View’ button in the ‘Default Key’ section. Listed in the pop up you shall see the Default Issuer and Default Key that you will need shortly.
Create a Model project
1, Open Visual Studio 2010 and create a new blank solution called MobileProducts. Add a new Class Library project to the solution called MobileProducts.Model. The target Framework should be .NET 4.0
The Listener and Calling projects need to understand what object is being passed over the service bus, so it makes sense to create a project where you will define all these objects in classes in a central place where they can be referenced.
2, Add a reference to System.Runtime.Serialization
3, Rename the existing Class1.cs file to Product.cs, and amend/add the following attributes and properties to the class:
[DataContract] public class Product { [DataMember] public int ProductId { get; set; } [DataMember] public string Name { get; set; } [DataMember] public decimal Price { get; set; } [DataMember] public int StockLevel { get; set; } }
I’m not going to list the using references you should add at the top of your code. If you get red squiggly lines when compiling, put your cursor on the code in question and do ctrl + . and Visual Studio will assist you in adding the necessary using references.
Creating the Listener project
1, Add a new Console application to the Visual Studio Solution called MobileProducts.Listener.
2, Ensure the project type is .NET Framework 4.0 (rather than .NET 4.0 client profile)
3, Add a reference to our local project MobileProducts.Model.
4, Add references to the necessary Azure libraries we will be using. There are:
System.ServiceModel.dll – this can be found from the standard .NET reference list
Microsoft.ServiceBus.dll – you’ll need to browse to C:Program FilesWindows Azure SDKv1.6ServiceBusrefMicrosoft.ServiceBus.dll to find it.
5, Add a Linq 2 SQL class to the project and call it Database.dbml, add the Products table from the simple database you setup and created earlier.
6, We want to create a method that can be called over the Service Bus that returns a list of products. We’ll use a contract/interface so add an interface class to your project called IGetData.cs and ensure it has the following code added:
[ServiceContract(Name = "MobileProductsContract", Namespace = "http://MobileProductsContract")] public interface IGetData { [OperationContract] IEnumerable<MobileProducts.Model.Product> GetProducts(); [OperationContract] MobileProducts.Model.Product GetProductById(int Id); }
7, Next we can add a class that implements this contract, add a class to the project called GetData.cs and implement the following code…
[ServiceBehavior(Name = "MobileProductsContract", Namespace = "http://MobileProductsContract")] public class GetData : IGetData { public IEnumerable<MobileProducts.Model.Product> GetProducts() { DatabaseDataContext db = new DatabaseDataContext(); IEnumerable<MobileProducts.Model.Product> products = from p in db.Products select new MobileProducts.Model.Product { Name = p.Name, Price = p.Price, ProductId = p.ProductId, StockLevel = p.StockLevel }; return products; } public MobileProducts.Model.Product GetProductById(int Id) { DatabaseDataContext db = new DatabaseDataContext(); var product = db.Products.SingleOrDefault(p => p.ProductId == Id); return new MobileProducts.Model.Product { Name = product.Name, Price = product.Price, ProductId = product.ProductId, StockLevel = product.StockLevel }; } }
8, In App.Config we need to describe the bindings we want to use, add the following just beneath the connection strings elements:
<system.serviceModel> <services> <service name="MobileProducts.Listener.GetData"> <endpoint contract="MobileProducts.Listener.IGetData" binding="netTcpRelayBinding" /> </service> </services> <extensions> <bindingExtensions> <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </bindingExtensions> </extensions> </system.serviceModel>
9, We can now write the code in Program.cs to connect to our service bus and start listening for calls to execute methods…add this code within the Main method:
Console.Title = "Mobile Products Listener Service"; string nsDmn = ""; string issrName = ""; string issrSecret = ""; Uri address = ServiceBusEnvironment.CreateServiceUri("sb", nsDmn, "MobileProducts"); TransportClientEndpointBehavior sharedCreds = new TransportClientEndpointBehavior(); sharedCreds.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issrName, issrSecret); try { ServiceHost serviceHost = new ServiceHost(typeof(GetData), address); IEndpointBehavior serviceRegSettings = new ServiceRegistrySettings(DiscoveryType.Public); foreach (ServiceEndpoint serviceEndpoint in serviceHost.Description.Endpoints) { serviceEndpoint.Behaviors.Add(sharedCreds); } serviceHost.Open(); Console.WriteLine("Service Information: " + address); Console.WriteLine("Hit [Enter] to exit application..."); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); Console.ReadLine(); }
10, Set the following variables:
nsDmn = the name you gave the Service Bus instance you created
issrName = the owner
issrSecret = the default key you got once your Service Bus was created
Create a test Caller project
It’s always good to take mini steps and test things a we go along. Eventually our caller project will be the ASP.NET MVC web application, but initially lets create another console application that will act as a caller. If we get any issues it will be much easier to debug each time, make sure we have the Service Bus component working, and then move onto the next stage of building a mobile web app.
1, Add a new console application to your solution called MobileProducts.Caller.
2, Change the project from .Net 4.0 Client Profile to standard .Net Framework 4.0 again.
3, Add a reference to our local project dll, MobileProduct.Model and also Microsoft.ServiceBus and System.ServiceModel
4, We want to define the contract again, so our program understands the method it will be calling over the Service Bus and the data it will be getting back. Add an interface class to the project called IGetData and add the same code as when we created the contract for our Listener class
[ServiceContract(Name = "MobileProductsContract", Namespace = "http://MobileProductsContract")] public interface IGetData { [OperationContract] IEnumerable<Product> GetProducts(); [OperationContract] Product GetProductById(int Id); }
5, Add an application configuration (app.config) file to the project and add the following service model definition:
<system.serviceModel> <client> <endpoint name="RelayEndpoint" contract="MobileProducts.Caller.IGetData" binding="netTcpRelayBinding" /> </client> <extensions> <bindingExtensions> <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions> </system.serviceModel>
6, Add the following code to the Main method of Program.cs
Console.Title = "Mobile Products Test Caller"; string nsDmn = ""; string issrName = ""; string issrSecret = ""; Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", nsDmn, "MobileProducts"); TransportClientEndpointBehavior sharedCreds = new TransportClientEndpointBehavior(); sharedCreds.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issrName, issrSecret); ChannelFactory<IGetData> channelFactory = new ChannelFactory<IGetData>("RelayEndpoint", new EndpointAddress(serviceUri)); channelFactory.Endpoint.Behaviors.Add(sharedCreds); IGetData channel = channelFactory.CreateChannel(); ((ICommunicationObject)channel).Open(); try { IEnumerable<MobileProducts.Model.Product> products = channel.GetProducts(); foreach (var product in products) { Console.WriteLine(product.Name); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } ((ICommunicationObject)channel).Close(); channelFactory.Close(); Console.WriteLine("Press [Enter] to continue..."); Console.ReadLine();
7, As with the Listener project, set the same values for nsDmn, issrName and issrSecret.
8, Assuming you have some products created in your database table, you can now try the Service Bus out!
First right click on the MobileProducts.Listener project and choose Debug -> Start New Instance
Wait for the Service Information to be displayed in the console window
9, Now right click on the MobileProducts.Caller project and also choose Debug -> Start New Instance
In the Caller console window you should see a list of product names from your database table! Woot Woot! This means the Caller program is calling the GetData method over the Service Bus, and the Listener is executing this method and returning the data back across the Bus. Pretty awesome!
The MobileProducts.Caller application has served its purpose. We can now move forward knowing the service bus is working and look at building a mobile application to surface our data.
Create a mobile web app
Now that we know our Service Bus is setup and working through using the Listener and Caller projects we built, we can move forward and build a Mobile Web Application. We might as well host this in Windows Azure as well.
1, Right click on the Solution in Visual Studio and choose to Add -> New Project
2, From the Cloud section pick the Windows Azure Project and call it MobileProducts.Azure
3, When presented with the list of Azure Projects to create, move the ASP.NET MVC 3 Web Role over.
4, Right click on the project when it is in the right hand pane and choose ‘Rename’. Give it a new name of MobileProducts.WebApp
5, In the ASP.NET MVC 3 new project dialog use the Empty project template, ensure Razor is selected as the view engine, and tick the box for using HTML 5 semantic markup
6, Lets do the backend work first and get our web application able to talk to the Service Bus. Add a reference to our local project MobileProducts.Model. Also add a reference to Microsoft.ServiceBus and System.ServiceModel. You should be able to easily pick these from the Recent tab in the ‘Add Reference…’ dialogue.
7, In solution explorer, right click on the Models folder in your MVC web app and add a new interface class called IGetData. Add the same code that we previously have done for this interface class:
[ServiceContract(Name = "MobileProductsContract", Namespace = "http://MobileProductsContract")] public interface IGetData { [OperationContract] IEnumerable<Product> GetProducts(); [OperationContract] Product GetProductById(int Id); }
8, Right click on the Models folder and choose to add a new class. Give the class a name of ProductWorker.cs
9, In the class add the following static methods:
public static IEnumerable<Product> GetProducts() { string nsDmn = ""; string issrName = ""; string issrSecret = ""; Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", nsDmn, "MobileProducts"); TransportClientEndpointBehavior sharedCreds = new TransportClientEndpointBehavior(); sharedCreds.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issrName, issrSecret); ChannelFactory<IGetData> channelFactory = new ChannelFactory<IGetData>("RelayEndpoint", new EndpointAddress(serviceUri)); channelFactory.Endpoint.Behaviors.Add(sharedCreds); IGetData channel = channelFactory.CreateChannel(); ((ICommunicationObject)channel).Open(); return channel.GetProducts(); } public static Product GetProductById(int Id) { string nsDmn = ""; string issrName = ""; string issrSecret = ""; Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", nsDmn, "MobileProducts"); TransportClientEndpointBehavior sharedCreds = new TransportClientEndpointBehavior(); sharedCreds.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issrName, issrSecret); ChannelFactory<IGetData> channelFactory = new ChannelFactory<IGetData>("RelayEndpoint", new EndpointAddress(serviceUri)); channelFactory.Endpoint.Behaviors.Add(sharedCreds); IGetData channel = channelFactory.CreateChannel(); ((ICommunicationObject)channel).Open(); return channel.GetProductById(Id); }
10, In the Views folder, right click and add a new folder called Home. Right click the Home folder and add a View called Index, and then add a View called Product
11, Right click the Controllers folder and add a new Controller called ‘HomeController’ created using the ‘Empty controller’ template
12, In HomeController.cs amend the code to look like this:
public ActionResult Index() { return View(ProductWorker.GetProducts()); } public ActionResult Product(int Id) { return View(ProductWorker.GetProductById(Id)); }
13, Open up web.config and paste in the following XML fragment just after the opening <Configuration>
<system.serviceModel> <client> <endpoint name="RelayEndpoint" contract="MobileProducts.WebApp.IGetData" binding="netTcpRelayBinding" /> </client> <extensions> <bindingExtensions> <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions> </system.serviceModel>
14, Open up Index.cshtml and paste in the following:
@model IEnumerable<MobileProducts.Model.Product> @{ ViewBag.Title = "Available Products"; } <h2>Available Products</h2> @foreach (var product in Model) { <p>@product.Name</p> }
15, Hurrah – it’s time to test again. Right click your Listener project and Debug – Start new instance, and once that has fired up and is connected to the Service Bus right click your MVC web app and do the same. You should get your list of products displayed.
Pretty simple, but it shows the data is coming through the Service Bus ok.
16, We want to make our application really nice to use on mobiles, so lets add in jQueryMobile. In Views -> Shared open _Layout.cshtml. Before the closing </head> tag add in:
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" /> <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
Also change jQuery so it is using the latest version:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
17, Final thing to add in the head section is:
<meta name="viewport" content="width=device-width, initial-scale=1">
18, Modify the html in your Index.cshtml file to look like this:
@model IEnumerable<MobileProducts.Model.Product> @{ ViewBag.Title = "Available Products"; } <div data-role="page"> <div data-role="header"> <h2>Available Products</h2> </div> <div data-role="content"> <ul data-role="listview" data-inset="true" data-filter="true"> @foreach (var product in Model) { <li><a href="/home/product/@product.ProductId" data-transition="pop">@product.Name</a></li> } </ul> </div> </div>
19, Open up Product.cshtml add get your code looking like:
@model MobileProducts.Model.Product @{ ViewBag.Title = "Product"; } <h2>@Model.Name</h2> <p>Price : @Model.Price</p> <p>Stock Level : @Model.StockLevel</p> <a href="#" data-rel="back">Back</a>
20, Go through the process of starting up your Listener project, and then your Web App. Hopefully the UI will look a lot nicer and more jQueryMobile like:
Now we are going to publish our web app to Azure so we can test it on our own mobiles.
Publish to Azure
Running locally is all good fun, but for our app to be viewable on mobile devices we want to publish it to Azure. Lets go do that…
1, Right click on MobileProducts.Azure in the solution explorer and choose ‘Package…’
2, Open up www.azure.com and log in. Click on ‘New Hosted Service in the ribbon
3, Give your hosted service a name, url, pick a region, deployment name – and then browse to the MobileProducts.Azure.cspkg and ServiceConfiguration.Cloud.cscfg files that Visual Studio created for you as part of the Packaging process.
4, The finally click OK. You can accept the warning that is displayed upon clicking OK as this is just a test app, but if it was a real production level app you’d want to take a bit more notice of it.
Once Azure has gone through the process of provisioning your new hosted service and uploading your application get out your smartphone and browse to the url you gave your application in Azure. Hopefully you will see something similar to below:
[Click for a larger image]
Check out how cool the search and page transitions are. jQueryMobile really is nice!
Things to think about
Authentication…We’ll be looking into this in future blog posts. At the moment our web app is open to the public so anybody can hit up our url. In most situation’s with company sensitive data we wouldn’t want this to happen.
Thanks for reading this far, hopefully this post has been useful and has got you excited about Azure and Mobile web apps. Any thoughts or feedback, please leave a comment or email nick@lightningtools.com
<nickswan />