Z Code
PCL & Azure How-To: Using Portable Class Libraries and AppFabric Service Bus To Create a Continuous Client
Here's how to build a simple continuous client application that spans multiple devices using the cloud Azure to handle communication between the devices.
I feel lucky to live in the days of continuously connected devices. I
love that I'm able to reply to e-mail using my phone while riding the bus
home. It's amazing to be able to Skype with my family on the other side of
the world and team up with like-minded gamers across the country on my
Xbox. However, in this world of permanent Internet connectivity, there is,
as Joshua Topolsky puts it, "a missing link in our computing experience"
(engt.co/9GVeKl).
This missing link refers to the lack of what Topolsky calls a
continuous client; that is, a solution to the broken workflow that occurs
today when you move from one device to another. As I switch among my PC,
tablet and phone in a typical day, my current browsing session, documents,
windows and application state should naturally flow to all of them. That
way, I'd spend less time on context switching and more time on actual work
and play.
In this article, I'll show you how to build a simple continuous client
application that spans multiple devices and platforms. I'll make use of
the new Portable Class Libraries (PCLs) to ease the development of a
cross-platform application, and the cloud—in particular Windows Azure
AppFabric Service Bus—to handle the communication between the devices.
On Your Way Home …
It's late afternoon and I'm at work trying to fix that last bug quickly
so I can avoid peak-hour traffic. The inevitable phone call comes: "Honey,
on your way home can you pick up some milk, bread and chickpeas?" I hang
up, get to the store and realize I've forgotten what to buy. In the end, I
head home with items we already have in the pantry. It's frustrating, and
today's solution tends to involve a lot of back-and-forth phone calling:
"Did you say frozen peas or chickpeas?" "Chickpeas. And while you're
there, can you buy toilet paper?"
To help alleviate our marriage tensions around this particular issue
(the others will have to wait for another day), I'll write a simple app
called "On Your Way Home" that runs on our Windows Phone-based devices and
Windows 8 beta tablets and allows my wife and me to easily track our
shopping list. It will keep us both informed, in real time, of any changes
to the shopping list so that at any time we know exactly what we need to
buy.
Given that a smartphone running Window Phone and a Windows 8-based
tablet are different devices, with differing flavors of the Microsoft .NET
Framework and Windows, I'll use PCLs to abstract away platform differences
and enable me to share as much application logic as possible, including
all the of the communication with the Windows Azure AppFabric Service Bus.
I'll also use the Model-View-ViewModel (MVVM) pattern (bit.ly/GW7l) to facilitate the use of the
same Models and ViewModels from our device-specific Views.
Portable Class Libraries
In the past, cross-platform development in the .NET Framework hasn't
been easy. While the .NET Framework had grand dreams as a cross-platform
runtime, Microsoft hasn't yet fully delivered on the promise. If you've
ever attempted to deliver a .NET Framework-based application or framework
that spanned multiple devices, you'll have noticed that a few things got
in the way.
On the Runtime Side The assembly factoring, versioning and assembly
names are different among the .NET platforms. For example, System.Net.dll
on the .NET Framework, which contains peer-to-peer networking APIs, means
something entirely different on Silverlight, where it contains the core
networking stack. To find those APIs on the .NET Framework, you'll need to
reference System.dll. The assembly versions are also not the same;
Silverlight adopts 2.0.5.0 for versions 2.0 to 4, whereas 2.0.0.0 and
4.0.0.0 were adopted for .NET Framework versions 2.0 to 4. These
differences have, in the past, prevented an assembly compiled for one
platform from running on another.
On the Visual Studio Side Right from the beginning you need to decide
which platform to target—the .NET Framework, Silverlight or Windows Phone.
Once that decision is made, it's extremely hard to move to or support a
new platform. For example, if you're already targeting the .NET Framework,
targeting the .NET Framework and Silverlight means creating a new project
and either copying or linking the existing files into that project. If
you're lucky, you might have factored your application in such a way that
platform-specific pieces are easily replaced. If not (and this is probably
more likely), you'll need to #if PLATFORM your way around each build error
until you have a clean build.
This is where the new PCLs can help. PCLs, available as a free add-on
to Visual Studio 2010 (bit.ly/ekNnsN) and built into Visual
Studio 11 beta, provide an easy way to target multiple platforms using a
single project. You can create a new PCL, choose the frameworks you'd like
to target (see Figure 1) and start writing code. Under
the covers, the PCL tools handle the API differences and filter
IntelliSense so you see only classes and members that are available and
work across all the frameworks you've selected. The resulting assembly can
then be referenced and run, without any changes, on all indicated
frameworks.
|
Figure 1. Portable Class Library
Target Frameworks |
Solution Layout
A typical way to organize a cross-platform app using a PCL is to have
one or more portable projects containing the shared components, and have
platform-specific projects for each platform that references these
projects. For this application, I'll need two Visual Studio solutions—one
created in Visual Studio 2010 (OnYourWayHome.VS2010) containing my Windows
Phone app and one created in Visual Studio 11 (OnYourWayHome.VS11)
containing my Windows Metro-style app. I need multiple solutions because
at the time of writing, the Windows Phone SDK 7.1 works only on top of
Visual Studio 2010, whereas the new Windows 8 tools are available only as
part of Visual Studio 11. There isn't (currently) a single version that
supports both. Don't despair, though; a new feature available in Visual
Studio 11 helps me out here. I'm able to open most projects created in the
earlier version without having to convert them to the new format. This
allows me to have a single PCL project and reference it from both
solutions.
Figures 2 and 3 show the project
layout for my application. OnYourWayHome.Core, a PCL project,
contains the models, view models, common services and platform
abstractions. OnYourWayHome.ServiceBus, also a PCL project, contains
portable versions of the APIs that will talk to Windows Azure. Both
projects are shared between the Visual Studio 2010 solution and Visual
Studio 11. OnYourWayHome.Phone and OnYourWayHome.Metro are
platform-specific projects targeting Windows Phone 7.5 and .NET for
Metro-style apps, respectively. These contain the device-specific views
(such as the pages in the application) and implementations of the
abstractions found in OnYourWayHome.Core and OnYourWayHome.ServiceBus.
|
Figure 2. Windows Phone Project Layout
in Visual Studio 2010 |
|
Figure 3. Windows Metro-Style App
Project Layout in Visual Studio 11 |
Converting Existing Libraries to PCLs
To communicate with Windows Azure, I downloaded the Silverlight-based
REST sample from servicebus.codeplex.com and
converted it to a PCL project. Some libraries are easier to convert than
others, but you'll inevitably run into situations where a given type or
method isn't available. Here are some typical reasons a given API might
not be supported in PCLs:
The API Isn't Implemented by All Platforms Traditional .NET Framework
file IOs, such as System.IO.File and System.IO.Directory, fall into this
bucket. Silverlight and Windows Phone use the System.IO.IsolatedStorage
APIs (though different from the .NET Framework version), whereas Windows 8
Metro-style apps use Windows.Storage.
The API Isn't Compatible Across All Platforms Some APIs look and feel
the same, but it's hard or impossible to write code against them in a
portable and consistent way. ThreadStaticAttribute, which enables static
fields to have a unique value for each thread, is an example. Though it's
present on both the Windows Phone and Xbox platforms, neither of their
runtimes supports it.
The API Is Considered Obsolete or Legacy These APIs either contain
behavior that's unlikely to be present on future platforms, or they've
been replaced by newer technologies. BackgroundWorker is an example of
this; it was replaced by Task and the new asynchronous program features in
Visual Studio 11 beta.
We Ran out of Time Most APIs weren't written with portability in mind.
We spend a significant amount of time going through each API to make sure
it can be programmed against in a portable manner. This might involve
tweaking or adding to the API to make it portable. Because of the time and
effort involved, in the first version of PCLs we made available on Visual
Studio Gallery, we prioritized the high-value, highly used APIs.
System.Xml.Linq.dll and System.ComponentModel.DataAnnotations.dll are
examples of APIs that weren't available in that first version but are now
available in the Visual Studio 11 beta release.
There are a couple of different ways of handling an API that falls into
one of these scenarios. Sometimes there's a simple replacement. For
example, Close methods (Stream.Close, TextWriter.Close and so forth) have
been deprecated in PCL and replaced with Dispose. In such cases, it's just
a matter of replacing a call to the former with the latter. But sometimes
it's a little harder and takes more work. One situation I encountered
while converting the Service Bus APIs involved the HMAC SHA256 hash code
provider. It isn't available in a PCL because of the cryptography
differences between Windows Phone and Metro-style apps. Windows Phone apps
use .NET-based APIs to encrypt, decrypt and hash data, while Metro-style
apps use the new native Windows Runtime (WinRT) APIs.
The code in particular that failed to build after the conversion was
the following:
using (HMACSHA256 sha256 = new HMACSHA256(issuerSecretBytes))
{
byte[] signatureBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(token));
signature = Convert.ToBase64String(signatureBytes);
}
To help bridge the gaps between the Phone crypto APIs and the WinRT
crypto APIs, I invented a platform abstraction representing the Service
Bus requirement. In this case, the Service Bus needed a way to calculate
an HMAC SHA256 hash:
public abstract class ServiceBusAdapter
{
public static ServiceBusAdapter Current
{
get;
set;
}
public abstract byte[] ComputeHmacSha256(byte[] secretKey, byte[] data);
}
I added ServiceBusAdapter to the portable project, as well as a static
property for setting the current abstraction, which will become important
later. Next, I created Windows Phone- and Windows 8-specific HMAC SHA256
implementations of this abstraction, and put these in their respective
projects, as shown in Listing 1.
At startup in the Windows Phone project, I then "bootstrapped" the
Service Bus by setting the Phone-specific adapter as the current
adapter:
ServiceBusAdapter.Current = new PhoneServiceBusAdapter();
I did the same for the Windows 8 project:
ServiceBusAdapter.Current = new MetroServiceBusAdapter();
With everything in place, I then changed the original non-compiling
code to call through the adapter:
var adapter = ServiceBusAdapter.Current;
byte[] signatureBytes = adapter.ComputeHmacSha256(issuerSecretBytes, Encoding.UTF8.GetBytes(token));
So although there are two different ways of computing the hash
depending on the platform, the portable project talks to both using a
single interface. This can take a little bit of work up front, but I can
easily reuse the infrastructure as I run into more APIs that need bridging
between the platforms.
As a side note, I used a static property to access and register the
adapter, which makes it easier to move existing APIs over to using the
adapter. If you're using a dependency injection framework such as the
Managed Extensibility Framework (MEF), Unity or Autofac, you'll find that
it's natural to register the platform-specific adapter into the container
and have the container "inject" the adapter into portable components that
need it.
Application Layout
My shopping list application, On Your Way Home, has two simple views:
ShoppingListView, which displays the current items on the shopping list;
and AddGroceryItemView, which allows a user to add more items to the list.
Figures 4 and 5 show the Windows Phone
versions of these views.
|
Figure 4. ShoppingListView |
|
Figure 5. AddGroceryItemView |
ShoppingListView shows all the items that are yet to be purchased, with
the idea that as you walk around the store, you check off each item as you
add it to the cart. After purchasing the items, clicking check out causes
the checked items to be taken off the list, indicating they no longer need
to be purchased. Devices sharing the same shopping list instantly (well,
as instantly as the network behind it allows) see changes made by another
person.
The Views, which live in the platform-specific projects, consist mainly
of XAML and have very little codebehind, which limits the amount of code
you need to duplicate between the two platforms. Using XAML data binding,
the Views bind themselves to portable ViewModels that provide the commands
and data that run the Views. Because there's no common UI framework that
ships across all platforms, PCL projects can't reference UI-specific APIs.
However, when targeting frameworks that support them, they can take
advantage of APIs that are typically used by ViewModels. This includes the
core types that make XAML data binding work, such as
INotifyPropertyChanged, ICommand and INotifyCollectionChanged. Also,
although the WinRT XAML framework doesn't support them,
System.ComponentModel.DataAnnotations and INotifyDataErrorInfo have been
added for completeness, and this enables custom XAML validation frameworks
to support portable ViewModels/Models.
Listing 2 and 3 show examples of
View/ViewModel interactions. Listing 2 shows the controls
on the Window Phone version of AddGroceryItemView and their bindings.
These controls are bound against the properties on the
AddGroceryItemViewModel, which is shared with both the Windows Phone and
Windows 8 projects, as shown in Listing 3.
Event Sourcing
On Your Way Home is based heavily around the concept of event sourcing
(bit.ly/3SpC9h). This is the idea that all
state changes to an application are published and stored as a sequence of
events. In this context, event doesn't refer to the thing defined by the
C# event keyword (although the idea is the same), but rather to concrete
classes that represent a single change to the system. These are published
through what's called an event aggregator, which then notifies one or more
handlers that do work in response to the event. (For more about event
aggregation, see Shawn Wildermuth's article, "Composite Web Apps with
Prism," at msdn.microsoft.com/magazine/dd943055.)
For example, the event that represents a grocery item being added to
the shopping list looks something like what's shown in Listing 4.
The ItemAddedEvent class contains information about the event: in this
case, the name of the grocery item that was added and an ID that's used to
uniquely represent the grocery item within a shopping list. Events are
also marked with [DataContract], which makes it easier for them to be
serialized to disk or sent over the wire.
This event is created and published when the user clicks the add button
on the AddGroceryItemView, as shown in Listing 5.
Note that this method doesn't directly make any change to the shopping
list; it simply publishes the ItemAddedEvent to the event aggregator. It's
the responsibility of one of the event handlers listening to this event to
do something with it. In this case, a class called ShoppingList subscribes
to and handles the event, as shown in Listing 6.
Every time ItemAddedEvent is published, ShoppingList creates a new
GroceryItem using the data from the event and adds it to the shopping
list. The ShoppingListView, which is indirectly bound to the same list via
its ShoppingListViewModel, is also updated. This means that when the user
navigates back to the shopping list page, the items he just added to the
list are shown as expected. The process of removing an item from the
shopping list, adding an item to a cart and checking out the cart are all
handled using the same event publish/subscribe pattern.
It may at first seem like a lot of indirection for something as simple
as adding items to a shopping list: the AddGroceryItemViewModel.Add method
publishes an event to the IEventAggregator, which passes it onto the
ShoppingList, which adds it to the grocery list. Why doesn't the
AddGroceryItemViewModel.Add method simply bypass the IEventAggregator and
add the new GroceryItem directly to the ShoppingList? I'm glad you asked.
The advantage of treating all state changes to the system as events is
that it encourages all the individual parts of the application to be very
loosely coupled. Because the publisher and subscribers don't know about
each other, inserting a new feature in the pipeline, such as syncing data
to and from the cloud, is a lot simpler.
Syncing Data to the Cloud
I've covered the basic functionality of the application running on a
single device, but there's still the problem of getting the changes a user
makes to the shopping list to other devices, and vice versa. This is where
the Windows Azure AppFabric Service Bus comes in.
Windows Azure AppFabric Service Bus is a feature that enables
applications and services to easily talk with each other over the
Internet, avoiding the complexities of navigating communication obstacles
such as firewalls and Network Address Translation (NAT) devices. It
provides both REST and Windows Communication Foundation (WCF) HTTP
endpoints hosted by Windows Azure and sits in between the publisher and
the subscriber.
There are three main ways to communicate using the Windows Azure
AppFabric Service Bus; for the purposes of my application, however, I'll
just cover Topics. For a full overview, check out "An Introduction to the
Windows Azure AppFabric Service Bus" at bit.ly/uNVaXG.
For publishers, a Service Bus Topic is akin to a big queue in the cloud
(see Figure 6). Completely unaware of who's listening,
publishers push messages to the Topic, where they're held ad infinitum
until requested by a subscriber. To get messages from the queue,
subscribers pull from a Subscription, which filters messages published to
the Topic. Subscriptions act like a particular queue, and messages removed
from a Subscription will still be seen from other Subscriptions if their
own filters include them.
[Click on image for larger view.] |
Figure 6. Service Bus
Topi |
In On Your Way Home, the AzureServiceEventHandler class is the bridge
between the application and the Service Bus. Similar to ShoppingList, it
also implements IEventHandler<T>, but instead of specific events,
AzureServiceEventHandlers can handle them all, as shown in Figure
7.
[Click on image for larger view.] |
Figure 7. The
AzureServiceEventHandler Clas |
public class AzureServiceBusEventHandler : DisposableObject, IEventHandler<IEvent>, IStartupService
{
private readonly IAzureServiceBus _serviceBus;
private readonly IAzureEventSerializer _eventSerializer;
public AzureServiceBusEventHandler(IEventAggregator eventAggregator,
IAzureServiceBus serviceBus, IAzureEventSerializer eventSerializer)
{
_eventAggregator = eventAggregator;
_eventAggregator.SubscribeAll(this);
_serviceBus = serviceBus;
_serviceBus.MessageReceived += OnMessageReceived;
_eventSerializer = eventSerializer;
}
[...]
public void Handle(IEvent e)
{
BrokeredMessage message = _eventSerializer.Serialize(e);
_serviceBus.Send(message);
}
}
Every change a user makes to the state of the shopping list is handled
by AzureServiceBusEventHandler and pushed directly to the cloud. Neither
AddGroceryItemViewModel, which publishes the event, nor ShoppingList,
which handles it on the local device, is aware that this happens.
The trip back from the cloud is where an event-based architecture
really pays off. When the AzureServiceEventHandler detects that a new
message has been received on the Service Bus (via the
IAzureServiceBus.MessageReceived C# event), it does the reverse of what it
did earlier and deserializes the received message back into an event. From
here, it gets published back via the event aggregator, which causes it to
be treated as though the event came from within the application, as shown
in Listing 7.
The ShoppingList isn't aware (nor does it care) about the source of the
event and handles those coming from the Service Bus/cloud as though they
came directly from a user's input. It updates its list of groceries, which
in turn causes any of the views bound to that list to be updated as
well.
If you pay special attention, you might notice one little problem with
the workflow: Events that get sent to the cloud from the local device come
back to that same device and cause duplication of the data. Worse, changes
to other, unrelated shopping lists will also come to that device. I don't
know about you, but I'm pretty sure I don't want to see other people's
food choices appearing on my shopping list. To prevent this, a Service Bus
Topic is created per list, and a Subscription per device, which listens to
the Topic. When the messages are published to the Topic from the device, a
property containing the device ID is sent along with the messages, which
the Subscription filter uses to exclude messages that came from its own
device. Figure 8 shows this workflow.
[Click on image for larger view.] |
Figure 8. Device-to-Device
Workflow |
Wrapping Up
I covered a lot in this article: Portable Class Libraries simplified my
solution and significantly reduced the amount of code I needed to write to
target the two platforms. Also, changing application state via events made
it very easy to sync that state with the cloud. There's still a lot I've
left unsaid, however, that you'll want to factor in when developing a
continuous client. I didn't talk about offline event caching and fault
tolerance (what if the network isn't available when I publish an event?),
merge conflicts (what if another user makes a change that conflicts with
mine?), playback (if I attach a new device to the shopping list, how does
it get updated?), access control (how do I prevent unauthorized users
accessing data they shouldn't?) and finally, persistence. In the sample
code for the article, the application doesn't save the shopping list
between launches. I'll leave this as an exercise for you; it might be an
interesting challenge if you want to play around with the code. A naïve
(or rather the traditional) way of approaching persistence might be to a
put a hook directly into the ShoppingList class, mark the GroceryItem
objects as serializable and save them off to a file. Before going down
this route, though, stop and think about it: Given that the ShoppingList
already handles events natively and already doesn't care where they come
from, syncing data to and from the cloud looks surprisingly like saving
and restoring data from disk, doesn't it?