다음을 통해 공유


Cloud + Device: Combine the power of Windows Azure, IE 9, and Windows Phone 7 (Part 3)

In the last post, we focused on the device side. We created a graphic rich
web application using HTML 5 canvas, and browsed it in IE 9. This post focuses
on the integration. That is, how to connect client applications to cloud
services.

You can see the application lively on

https://smalldemos.cloudapp.net/Html5Demos/SvgDemo.htm. The source code for
this project can be downloaded from 1Code using this link: https://1code.codeplex.com/releases/view/51868#DownloadId=152535.

Working with SVG

The UI of this demo is much simpler than the demo in the last post. It simply
draws a few rectangles, which looks like a 3D bar chart. The rectangles are
rendered using SVG. You can refer to the

IE 9 Developers Guide to get started with SVG.

SVG is a retained graphics API. It is much easier to use than canvas. It also
supports declaratively creating shapes, just like Silverlight's XAML.

To embed SVG contents in an HTML document, you put a svg tag with the proper
namespace:

     <svg id="mainSvg" version="1.1" xmlns="https://www.w3.org/2000/svg">

    </svg>

Then you can write xml tags to define the shapes. For example:

<rect fill="orange" stroke="black" width="150" height="75" x="50" y="25" />

This sample creates the elements dynamically in code based on the data
returned by cloud services. To do so, you can use the familiar DOM API. For
example:

             var rectangle1 = document.createElementNS(svgNamespace, 'rect');

            rectangle1.setAttribute('width', 100);

            rectangle1.setAttribute('height', height * 200);

            var x1 = 30 + i * 180;

            var y1 = 770 - height * 200;

            rectangle1.setAttribute('x', x1);

            rectangle1.setAttribute('y', y1);

            rectangle1.setAttribute('fill', 'rgb(' + red + ',' + green + ',' + blue + ')');

            rectangle1.setAttribute('stroke', 'black');

            rectangle1.setAttribute('stroke-width', 2);
             document.getElementById('mainSvg').appendChild(rectangle1);

As you can see, the code is not so straightforward as a typical Silverlight
code, because JavaScript does not pre-define classes like Rectangle. This sample
shows you the raw code. In real world, when working with SVG in code, it is
recommended to use a library like
jQuery SVG.

The advantage of SVG over canvas is it remembers the rectangle you just
created. While not demonstrated in the sample, you can change the rectangle's
properties (such as width and height) at a later time, and you can add mouse
events on the rectangle. For example, when clicking the rectangle, you change
its color. With an immediate graphics API (like canvas), it takes much more work
to add interactives.

Connecting the application to the cloud

Now that you understand the basics of SVG, we can move to the cloud. The bars
in the demo actually represents how dangerous a city is (in the New York state).
The more dangerous a city is, the higher the bar becomes.

Obtaining professional data from Dallas

We do not store the data in our own database. Instead, we obtain the data
from Dallas, a service hosted on Windows Azure. Dallas allows you to consume
data provided by professional data providers. For example, our sample uses the
"2006 and 2007 Crime in the United States" service provided by DATA.gov. Those
professional data providers give you the most authority data.

The role of Dallas is: First, serves as a discovery portal for you to
discover those services. Second, exposes the data to you using a single
protocol: OData. So with Dallas, you don't need to learn a separate API to
consume each service. You can use the familiar WCF Data Services API.

Please refer to
https://www.microsoft.com/windowsazure/dallas/ for more information about
Dallas.

Accessing Dallas data

Since Dallas exposes the data using OData protocol, you can use the standard
REST API or a client library listed on

https://www.odata.org/developers/odata-sdk (such as the JavaScript library
and the Windows Phone library) to directly access the data in a client
application. However, this approach exposes two problems: First, it may work for
simple scenarios. But what if you want to write some custom business logics on
top of the data? Where do you put the business logic? Duplicate them in all
client applications? Second, you have to embed your Dallas account key in the
client application, which is very dangerous.

So we will take a more SOA approach. We host a WCF REST service in Windows
Azure. Then the client applications work with the WCF service. The service also
performs some custom business logics. It calculates how dangerous a city is
based on a simple rule (see the int sum = line of code for details).

         public List<CityCrimeWeight> GetCrimes()

        {

            List<CityCrimeWeight> results = new List<CityCrimeWeight>();

            datagovCrimesContainer svc = new datagovCrimesContainer(new Uri("https://api.sqlazureservices.com/Data.ashx/data.gov/Crimes"));

            svc.Credentials = new NetworkCredential("Account Key", "Your account key");

            var query = from c in svc.CityCrime where c.State == "New York" select c;

            int count = query.Count();

            int queried = 0;

            while (queried <= count)

            {

                var pagedQuery = query.Skip(queried).Take(100);

                queried += 100;

                foreach (var city in pagedQuery)

                {

                    CityCrimeWeight ccw = new CityCrimeWeight() { City = city.City, Population = city.Population };

                    int sum = city.MurderAndNonEgligentManslaughter * 10 + city.ViolentCrime * 7 + city.ForcibleRape * 7 + city.Arson * 7 + city.Burglary * 3 + city.MotorVehicleTheft * 3 + city.PropertyCrime * 1 + city.Robbery * 5 + city.LarcenyTheft * 1 + city.AggravatedAssault * 3;

                    ccw.Weight = (double)sum / city.Population;

                    results.Add(ccw);

                }

            }

            return results.OrderByDescending(c => c.Weight).Take(10).ToList();

        }

As you can see from the above code, accessing Dallas service is the same as
accessing a normal WCF Data Services. First add a service reference to generate
a client proxy. Then write a LINQ query, which will be translated to a
corresponding URI.

One thing to note is Dallas forces a server side paging. One request returns
up to 100 results. To obtain all results for a certain query, you should write a
paging logic. You do so by invoking the Skip method to skip several pages, and
invoke the Take method to specify the page size.

All requests to Dallas must be authenticated. You do so by setting the
service context's Credential property.

One final note is our service doesn't return all data obtained from Dallas.
We only return the city name, the population, and the weight we calculated using
our custom business logic. This also helps to reduce the message size returned
to the client. We host the service in the north central US data center, the same
as Dallas. Data exchanged within the same data center is very fast (faster than
disk I/O), and it doesn't charge for network consumption. So if you run the
application locally in Development Fabric, you may find the performance is not
so good as running in the cloud, because all Dallas data have to be downloaded
to your local machine. In the cloud, Dallas data is transferred from Dallas VM
to our own hosting VM between the same data center, which is very fast.

Hosting the WCF REST Services

Hosting a WCF REST Services in Windows Azure is the same as hosting it
locally. First we define a service contract with the WebGet/Invoke attribute.
Since we want to support a JavaScript client, we naturally return the data in
JSON format:

     [ServiceContract]

    public interface ICrimeService

    {

        [OperationContract]

        [WebGet(UriTemplate = "/Crimes", ResponseFormat = WebMessageFormat.Json)]

        List<CityCrimeWeight> GetCrimes();

    }

When using .NET 4, WCF configuration is much simpler. You can use
protocolMapping to define webHttpBinding as the default binding when the scheme
is http. You can define a nameless endpoint behavior which will be applied to
all endpoints. You don't need to explicitly define the services.

     <protocolMapping>

      <add binding="webHttpBinding" scheme="http"/>

    </protocolMapping>
 
     <behaviors>
       <endpointBehaviors>

        <behavior>

          <webHttp/>

        </behavior>

      </endpointBehaviors>

    </behaviors>

One problem in Windows Azure is certain configuration sections, such as
protocolMapping, are missing in machine.config on the cloud machines. So you
have to define them in web.config, otherwise the deployment will stuck in busy.

   <configSections>

    <sectionGroup name="system.serviceModel" type="System.ServiceModel.Configuration.ServiceModelSectionGroup, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

      <section name="standardEndpoints" type="System.ServiceModel.Configuration.StandardEndpointsSection, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

      <section name="protocolMapping" type="System.ServiceModel.Configuration.ProtocolMappingSection, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

    </sectionGroup>

  </configSections>

Consuming the service from JavaScript client

You can use the standard jQuery ajax method to consume the service:

             $.ajax({ url: '../Services/CrimeService.svc/Crimes', success: createChart, dataType: 'json' });
         function createChart(data)

        {

            // Create a simple bar chart.

            $(data).each(function (i)

            {

                createRectangle(i, this.Weight, this.City);

            });

            $('#placeHolderDiv').hide();

        }

Creating a Windows Phone client

Now we have a service hosted in Windows Azure. We have a client application
delivered from Windows Azure to a PC device, where a modern browser is running
to render the chart. The same application should also work fine on a phone that
supports SVG.

But not all phones support SVG. And even if SVG is supported, we need to
modify the UI to make it more phone friendly. To take full advantage of a
device, we still need to look into the native application development model.

On Windows Phone 7, an application can be created either using Silverlight or
XNA. XNA is usually used for games, while Silverlight is used for other
applications. So we choose Silverlight to implement our Windows Phone client.

We assume most Windows Azure developers are already familiar with
Silverlight. So we won't go into the details on how the application is created.
In addition, we cannot provide a live demonstration for the Windows Phone
client... So instead, we will ship the source code in 1Code, together with the
source code for the service and the HTML client.

Conclusion

This is the last post on IE 9/Windows Phone integration with Windows Azure.
The key take away of this series is the cloud wants smarter devices. Both
Windows 7 PC and Windows Phone 7 are smart devices. To exploit the full power of
the devices in your applications, you should choose a modern platform with a
modern technology. Then you connect the devices to the cloud, and bring the user
a better experience.