Windows Phone 7 Line of Business App Dev :: Improving the In-Memory Database

Phone 7

About a month ago, I wrote an article intended to help you fill some of the gaps left by the missing SQL Server Compact database.

Since your Windows Phone 7 Silverlight app is consuming an ObservableCollection of objects streaming down from Windows Azure and SQL Azure, it makes sense to organize those objects in a database-like format that’s easy to work with.  If you’ve ever worked with Remote Data Access (RDA) in the past, the notion of pre-fetching multiple tables to work with locally should look familiar.

In this case, each ObservableCollection represents a table, each object represents a row, and each object property represents a column.  I had you create a Singleton class to hold all these objects in memory to serve as the database.  The fact that Silverlight supports Language Integrated Query (LINQ) means that you can use SQL-like statements to work with the multiple, ObservableCollections of objects.

If you’re wondering why I have you cache everything in memory in a Singleton, there’s a few reasons.  For starters, it makes it easy to query everything with LINQ with the fastest performance possible for single and multi-table JOINs.  Secondly, I don’t represent a Microsoft product group and therefore wouldn’t engineer an unsupported provider that can query subsets of serialized data from files residing in Isolated Storage.  Finally, I don’t want you to accidentally find yourself with multiple instances of the same ObservableCollection when pulling data down from Azure or loading it from Isolated Storage.  Forcing everything into a Singleton prevents you wasting memory or updating objects in the wrong instance of an ObservableCollection.  An inconsistent database is not a good thing.  Don’t worry, you can control which tables are loaded into memory.

So what is this article all about and what are the “improvements” I’m talking about?

This time around, I’m going to focus on saving, loading and deleting the serialized ObservableCollections from Isolated Storage.  In that last article, I showed you how to serialize/de-serialize the ObservableCollections to and from Isolated Storage using the XmlSerializer.  This made it easy for you to save each table to its own XML file which sounds pretty cool.

So what’s wrong with this?

Saving anything as XML means that you’re using the largest, most verbose form of serialization.  After hearing me preach about the virtues of doing SOA with WCF REST + JSON, using the XmlSerializer probably seems out of place.  Luckily, the DataContractJsonSerializer supported by Silverlight on Windows Phone 7 gives you the most efficient wire protocol for data-in-transit can also be used to save those same .NET objects to Isolated Storage.  So the first improvement in this article comes from shrinking the size of the tables and improving the efficiency of the serialization/de-serializing operations to Isolated Storage using out-of-the-box functionality.

While going from XML to JSON for your serializing might be good enough, there’s another improvement in the way you write the code that will make this much easier to implement for your own projects.  A look back to the previous article reveals a tight coupling between the tables that needed to be saved/loaded and the code needed to make that happen.  This meant that you would have to create a SaveTable and LoadTable method for each table that you wanted to retrieve from Azure.  The new code you’re about to see is generic and allows you to use a single SaveTable and LoadTable method even if you decide to download 100 tables.

Enough talk already, let’s see some code.  Launch your ContosoCloud solution in Visual Studio and open Database.cs.  I want you to overwrite the existing code with the code shown below:

using System;
using System.Net;
using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization.Json;

namespace ContosoPhone
{
sealed class Database
{
//Declare Instance
private static readonly Database instance = new Database();

        //Private Constructor
private Database() { }

        //The entry point into this Database
public static Database Instance
{
get
{
return instance;
}
}

        //Serialize ObservableCollection to JSON in Isolated Storage
public void SaveTable<T>(T tableToSave, string tableName)
{
if (tableToSave != null)
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = store.CreateFile(tableName + “.txt”))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
serializer.WriteObject(stream, tableToSave);
}
}
}
else
{
throw new Exception(“Table is empty”);
}
}

        //Deserialize ObservableCollection from JSON in Isolated Storage
public T LoadTable<T>(T tableToLoad, string tableName)
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(tableName + “.txt”))
{
using (IsolatedStorageFileStream stream = store.OpenFile(tableName + “.txt”, System.IO.FileMode.Open))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
}
}
else
{
throw new Exception(“Table not found”);
}
}
}

        //Delete ObservableCollection from Isolated Storage
public void DropTable(string tableName)
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(tableName + “.txt”))
{
store.DeleteFile(tableName + “.txt”);
}
else
{
throw new Exception(“Table not found”);
}
}
}


//Declare Private Table Variables
private ObservableCollection<Customer> customerTable = null;

        //Customer Table
public ObservableCollection<Customer> Customers
{
get { return customerTable; }
set { customerTable = value; }
}
}
}

 

Looking from top to bottom, the first change you’ll notice is the new SaveTable method where you pass in the desired ObservableCollection and table name in order to serialize it as JSON using the DataContractJsonSerializer.  The next method down the list is LoadTable where you pass in the same parameters as SaveTable but you get back a de-serialized ObservableCollection.  The last new method in the Database Singleton is DropTable which simply deletes the serialized table from Isolated Storage if you don’t need it anymore.

So how do you call this code?

Bring up MainPage.xaml.cs, and find the click event for Save button.  Delete the existing XmlSerializer code and replace it with the following:

try
{
Database.Instance.SaveTable<ObservableCollection<Customer>>(Database.Instance.Customers, “Customers”);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

The code above shows you how to call the SaveTable method in the Singleton with the appropriate syntax to pass in the ObservableCollection type as well as actual ObservableCollection value and name.

Now find the click event for the Load button, delete the existing code and paste in the following:

try
{
Database.Instance.Customers = Database.Instance.LoadTable<ObservableCollection<Customer>>(Database.Instance.Customers, “Customers”);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

This code looks pretty much the same as the SaveTable code except that you set Database.Instance.Customers equal to the return value from the method.  For completeness sake, drop another button on MainPage.xaml and call it Drop.  In its click event, paste in the following code:

try
{
Database.Instance.DropTable(“Customers”);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

For this code, just pass in the name of the table you want to delete from Isolated Storage and it’s gone.

It’s time to hit F5 so you can see how things behave.

phone7

When your app comes to life in the emulator, I want you to exercise the system by Getting, Adding, Updating and Deleting Customers.  In between, I want you to tap the Save button, close the app, reload the app and tap the Load button and then View Customers to ensure you’re seeing the list of Customers you expect.  Keep in mind that when you Save, you overwrite the previously saved table.  Likewise, when you Load, you overwrite the current in-memory ObservableCollection.  Additionally, Saving, Loading, and Dropping tables that don’t exist should throw an appropriate error message.

So what’s the big takeaway for these tweaks I’ve made to the in-memory database?

While switching serialization from XML to JSON is a great improvement in size and efficiency, I truly believe that making the SaveTable and LoadTable methods generic and reusable will boost developer productivity.  The new ease with which you can Save and Load 1, 10 or even 1,000 tables makes this more attractive to mobile developers that need to work with local data.

So where do we go from here?

You now have some of the basic elements of a database on Windows Phone 7.  You don’t have ACID support, indexes, stored procedures or triggers but you have a foundation to build on.  So what should be built next?

To help ensure database consistency, I would add an AutoFlush feature next.  SQL Server Compact flushes its data to disk every 10 seconds and there’s nothing to prevent you from using the SaveTable method to do the same.  A timer set to fire at a user-specified interval that iterates through all the ObservableCollections and saves them will help keep your data safe from battery loss and unforeseen system failures.  The fact that your app can be tombstoned at any moment when a user taps the Back button makes an AutoFlush feature even more important.

Anything else?

At the beginning of this article I mentioned RDA which is a simple form of data synchronization.  It’s simple because it only tracks changes on the client but not the server.  To find out what’s new or changed on the server, RDA requires local tables on the device to be dropped and then re-downloaded from SQL Server.  With the system I’ve built and described throughout this series of articles, we already have this brute force functionality.  So what’s missing is client-side change tracking.  To do this, I would need to add code that fires during INSERTS, UPDATES, and DELETES and then writes the appropriate information to local tracking tables.  To push those changes back to SQL Azure, appropriate code would need to call WCF REST + JSON Services that execute DML code on Windows Azure.

I hope with the improvements I’ve made to the in-memory database in this article, you’ll feel even more empowered to build occasionally-connected Windows Phone 7 solutions for consumers and the enterprise.

Keep coding!

-Rob

Sharing my knowledge and helping others never stops, so connect with me on my blog at http://robtiffany.com , follow me on Twitter at https://twitter.com/RobTiffany and on LinkedIn at https://www.linkedin.com/in/robtiffany

Sign Up for my Newsletter and get a FREE Chapter of “Mobile Strategies for Business!”

[mc4wp_form id=”5975″]

Windows Phone 7 Line of Business App Dev :: Working with an In-Memory Database

Phone3

In my last article of this series, you finally got to consume wireless-friendly WCF REST + JSON Services from both Windows Server and Windows Azure with data coming from SQL Server/SQL Azure.

You now have an ObservableCollection of Customer objects residing in a Singleton on your Windows Phone 7 device.  This Singleton looks similar to an in-memory database and the Customers property works like a table.

So now what?

If you’re like me, you probably want to display the list of Customers in the UI.  You might also want to perform other local operations against this data store.  You could add a new Customer and update or even delete an existing one.

I’m going to apologize in advance for not doing the MVVM thing that everyone seems to be into these days and get right to the point. Drag a button on to MainPage.xaml and call it View Customers.  While you’re at it, drag a listbox below the button and name it listBoxCustomers.  Double-click on this Button and add the following code to the click event:

try
{
if (Database.Instance.Customers != null)
{
listBoxCustomers.DisplayMemberPath = “Name”;
listBoxCustomers.ItemsSource = Database.Instance.Customers;
}
else
{
MessageBox.Show(“The Customer Table is Empty”);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

In the simple code above, you set the listbox’s ItemsSource equal to the Customer collection in the Database Singleton and set the DisplayMemberPath property equal to the Name property of the Customer objects.

Hit F5 to start debugging this Windows Phone 7 + Azure solution.  As usual, a web page and the emulator will launch.  Tap the Get Customers button to pull the Customer data back from the WCF REST service.  Next, tap on the View Customers button to display the list of Customers from the in-memory database as shown in the picture below:

phone1

Now it’s time to add a new Customer so drop a button underneath the listbox and call it Add Customer.  Creating a new Customer object requires setting values for 8 properties.  Instead of having you add 8 textboxes to type in the info, I’ll keep in simple and let you add it in code.  In the click event of the button, paste in the code you see below:

try
{
if (Database.Instance.Customers != null)
{
Customer customer = new Customer();
customer.CustomerId = 5;
customer.DistributionCenterId = 1;
customer.RouteId = 1;
customer.Name = “ABC Corp”;
customer.StreetAddress = “555 Market Street”;
customer.City = “Seattle”;
customer.StateProvince = “WA”;
customer.PostalCode = “98987”;
Database.Instance.Customers.Add(customer);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

After setting all the properties (Columns), you add the Customer object (Row) to the Customers property (Table), in the Singleton Database.  Hit F5, tap the Get Customers button, tap the View Customers button and then tap the Add Customer button.  Through the magic of simple data-binding, you should see the new “ABC Corp” show up in the listbox as shown below:

phone2

Now that you’ve added a new Customer, it’s time to update it because the president of the company decided to change the name.  Drag a new button and drop it underneath the Add Customer button.  Call it Update Customer and in it’s click event, paste in the following code:

try
{
if (Database.Instance.Customers != null)
{
foreach (Customer c in Database.Instance.Customers)
{
if (c.Equals((Customer)listBoxCustomers.SelectedItem))
{
c.Name = “XYZ Inc”;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

The code above loops through the Customers ObservableCollection until it finds a match for the item that’s been selected in the listbox.  When it finds that match, it updates the Name property to “XYZ Inc” which will automatically update what the user views in the listbox.

Hit F5, tap the Get Customers button, tap the View Customers button and then tap the Add Customer button.  Now tap on “ABC Corp” in the listbox to highlight it.  Clicking the Update Customer button will change it before your eyes.

phone3

It turns out that “XYZ Inc” went out of business because the president was an idiot so you need to delete it.  Guess what, you need yet another button beneath the Update Customer button.  Call it Delete Customer and in it’s click event, paste in the following code:

try
{
if (Database.Instance.Customers != null)
{
Database.Instance.Customers.Remove((Customer)listBoxCustomers.SelectedItem);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

In the code above, the Customer object that matches the item selected in the listbox is removed from the Customers ObservableCollection.  Pretty simple stuff in this case.

To find out for sure, hit F5, tap the Get Customers button, tap the View Customers button and then tap the Add Customer button.  Now tap on “ABC Corp” in the listbox to highlight it.  Clicking the Update Customer button will change it to “XYZ Inc.”  Highlighting “XYZ Inc” and clicking the Delete Customer button will cause this defunct company to disappear as shown below:

phone4

Now suppose you only want to display the Customers from Seattle and not the Eastside.  A little LINQ will do the trick here.  Drag and drop a new button called Seattle next to the Test Uri button and paste the following code in the click event:

if (Database.Instance.Customers != null)
{
IEnumerable<Customer> customers = from customer in Database.Instance.Customers
where customer.DistributionCenterId == 1
select customer;
listBoxCustomers.DisplayMemberPath = “Name”;
listBoxCustomers.ItemsSource = customers;
}
else
{
MessageBox.Show(“The Driver Table is Empty”);
}

In the code above, I set an IEnumerable<Customer> variable equal to the Customers table where the DistributionCenterId is equal to 1.  Since the DistributionCenter #1 serves the Seattle area, I know the listbox will be filled with just Adventure Works LLC and City Power & Light.  Start debugging and test it for yourself.

The last thing you need to do with local data is store it offline since you can’t always count on the network being there.  Luckily we’ve got Isolated Storage to serialize stuff.  In order to work with Isolated Storage, I need you to add using System.IO.IsolatedStorage; at the top of the class.  Since in this example I’ll demonstrate XML Serialization of the Customers ObservableCollection, I’ll need you to add a reference to System.Xml.Serialization and then add using System.Xml.Serialization; at the top of the class.  With that plumbing in place, let’s write some actual code.

Drag and drop another button called Save next to the Get Customers button and paste the following code in the click event:

if (Database.Instance.Customers != null)
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = store.CreateFile(“Customers.xml”))
{
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Customer>));
serializer.Serialize(stream, Database.Instance.Customers);

        }
}
}

In the code above, you use the combination of IsolatedStorageFile and IsolatedStorageFileStream to write data to Isolated Storage.  In this case, you’re going to create an XML file to save the Customers ObservableCollection of Customer objects.  Remember back in the 80’s when databases used to save each table as an individual file?  I’m thinking of DBase III+, FoxPro, and Paradox at the moment.  Anyway, this is exactly what happens here using the power of the XmlSerializer.  Feel free to debug and step through the code to ensure that it executes without error.

To complete the picture, you need to be able to retrieve the Customers ObservableCollection from Isolated Storage and work with the data without ever having to call the WCF REST services.  Drag and drop one last button called Load next to the View Customers button and paste the following code in the click event:

using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(“Customers.xml”))
{
using (IsolatedStorageFileStream stream = store.OpenFile(“Customers.xml”, System.IO.FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Customer>));
Database.Instance.Customers = (ObservableCollection<Customer>)serializer.Deserialize(stream);
}
}
}

The code above does the reverse of the previous Save code by opening the Customers.xml and re-hydrating those objects back into the Customers table using the XmlSerializer.  This is pretty cool stuff.

To make it all real, hit F5 to start debugging this completed mobile project.  When the app loads, tap the Get Customers button to retrieve the data and then tap View Customers to verify that you can see the data.  Now I want you to tap the Save button to serialize the Customers to an XML file in Isolated Storage.  Lastly, I want you to tap on the Back button to close the application.

Hit F5 again to fire up the application.  This time, I don’t want you to retrieve the data from Azure.  Instead, I want you to tap the Load button to de-serialize the Customer objects from XML.  Now for the moment of truth.  Tap the View Customers button to see if you’re actually working with an offline database.  Hopefully, your app will look like the picture below:

phone5

Congratulations!  You’ve made it to the end of this series of articles on building an occasionally-connected Windows Phone 7 application that works with WCF REST + JSON Services in Windows Azure.  Furthermore, you’re now pulling data down from SQL Azure and you’re working with it locally using a simple in-memory database that you built yourself.  I’m hoping that you’ll now feel empowered to go build industrial-strength mobile solutions for your own company or your customers.

Keep coding!

-Rob

Sharing my knowledge and helping others never stops, so connect with me on my blog at http://robtiffany.com , follow me on Twitter at https://twitter.com/RobTiffany and on LinkedIn at https://www.linkedin.com/in/robtiffany

Sign Up for my Newsletter and get a FREE Chapter of “Mobile Strategies for Business!”

[mc4wp_form id=”5975″]

Windows Phone 7 Line of Business App Dev :: Consuming an Azure WCF REST + JSON Service

Windows Phone 7.5

In my last two articles, I showed you how to build WCF REST services using Visual Studio 2010 that can reside on-premise in Windows Server 2008 or in the Cloud in Windows Azure.  Furthermore, I demonstrated pulling data from a table in SQL Server/SQL Azure.  I serialized .NET Objects using lightweight JSON to speed data transfers over even the slowest wireless data networks.  Now it’s time to call that REST service from Windows Phone 7.

Launch VS2010 and open the solution you created to build the WCF Service Web Role in Azure last time.  Right-click on the solution and add a Windows Phone Application project.  Change the name to ContosoPhone.

WP71

Part of the magic of making all this work is to have both the Azure Development Fabric and the Windows Phone 7 project start when it comes time to debug.  Most developers are accustomed to only having a single startup project so let’s make sure you have everything we need running when you hit F5.  Right-click on the solution and select Properties.  Select Startup Project and then click on the Multiple startup projects radio button.  Set both the AzureRestService and ContosoPhone projects Action value to Start and click OK as shown below:

WP72

With the startup configuration complete, the next thing I want you to do is copy Customer.cs from the AzureRestService project to the new ContosoPhone project since you’ll need it to create local Customer objects when you retrieve the data from the WCF call.  In this new Customer class you’ll need to change the Namespace to ContosoPhone and remove using System.Web; at the top of the class.  In order to support the DataContract() and DataMember() attributes, you’ll need to add a reference to System.Runtime.Serialization in order to get it to compile.

Drag a Button on to your MainPage.xaml and call it Test.  Double-click on this Button and add the following WebClient code to the click event:

try
{
WebClient webClient = new WebClient();
Uri uri = new Uri(“
http://127.0.0.1:48632/service1.svc/getdata?number=8″);
    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedTest);
webClient.OpenReadAsync(uri);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

As you can see above, I create a WebClient and a Uri object that points to the local Url presented by the Azure Development Fabric.  The port number my be different on your machine so double-check.  This is the test REST service from the last article used to prove that you’re calls are making it through.  You’ve made this call with your web browser and Windows Phone 7 will call it the same way.  Since all calls are asynchronous and the WebClient above created an event handler, copy the following code into the MainPage class and make sure to add using System.IO; at the top:

void OpenReadCompletedTest(object sender, OpenReadCompletedEventArgs e)
{
try
{
StreamReader reader = new System.IO.StreamReader(e.Result);
MessageBox.Show(reader.ReadToEnd().ToString());
reader.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

In the event handler above, I use the StreamReader to grab the entire XML return value.  The fact that I’m using WebClient means that callbacks are run on the UI thread thus relieving you of the need to use the dispatcher to update the UI.  This allows you to display the following XML string in the MessageBox:

<string xmlns=”http://schemas.microsoft.com/2003/10/Serialization/”>You entered: 8</string>

It’s time to try this thing out so hit F5.  If it compiles and everything executes properly, you should see both a web browser pointing to the root of your Azure services as well as your emulator and you can test your services with both.  Click the simple Test Button on your Silverlight MainPage and the MessageBox should pop up after the XML result is returned from Azure displaying the answer:

WP73

Click the Back button on the Emulator to close your app and close the web browser window to shut down Azure.

Now it’s time to do something a little more ambitious like return the JSON-encoded list of Customers from SQL Azure.  Unlike most books and articles on Silverlight and RIA Services that you may have read, I’m not going to return this data and immediately data-bind it to a control.  As a long-time mobile and wireless guy that understands intermittent connectivity and the importance of an offline data store, I’m going to have you put this data in a local database first.  Since SQL Server Compact is nowhere to be found and I don’t want you going off and using a 3rd party or open source embedded database, I’m going to show you how to create a simple one of your own.

Right-click on your ContosoPhone project and select Add | Class.  Name this new class Database.

WP74

You’re going to turn this class into a Singleton to create an in-memory database which will ensure that only instance is available.  For simplicity’s sake, just copy the code below into the new Database class:

using System;
using System.Net;
using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace ContosoPhone
{
sealed class Database
{
//Declare Instance
private static readonly Database instance = new Database();

        //Private Constructor
private Database() { }

        //The entry point into this Database
public static Database Instance
{
get
{
return instance;
}
}

        //Declare Private Variables
private ObservableCollection<Customer> customerTable = null;

        //Customer Table
public ObservableCollection<Customer> Customers
{
get { return customerTable; }
set { customerTable = value; }
}
}
}

As you can see above, I’ve had you create a sealed class with a private, static, readonly constructor as an entry point following a popular .NET method of implementing a Singleton.  The big thing here is the Customers property of type ObservableCollection<Customer>.  Since each Customer object is a Row with properties that are Columns, then it’s easy to think of this ObservableCollection of these Customer objects as a Table.  You now have the beginnings of a simple, in-memory database with which to store all the Customer objects when they arrive from the Cloud.

Start out by dragging another button on to MainPage.xaml and calling it Get Customers.  Double-click on this Button and add the following WebClient code to the click event:

try
{
WebClient webClient = new WebClient();
Uri uri = new Uri(“
http://127.0.0.1:48632/service1.svc/customers”);
    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedCustomers);
webClient.OpenReadAsync(uri);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

You’ll notice that it looks almost identical to the previous WebClient call except this time you’re just adding /customers to the end of service1.svc.  The beauty of the UriTemplate is that Microsoft makes it just that easy to call a REST service from WCF.  Since the above code created an event handler, paste this following code into the MainPage class to handle the callback:

void OpenReadCompletedCustomers(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;

    try
{
ser = new DataContractJsonSerializer(typeof(ObservableCollection<Customer>));
Database.Instance.Customers = ser.ReadObject(e.Result) as ObservableCollection<Customer>;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

You’ll immediately see a bunch of squiggly lines telling you something is missing.  Add a reference to System.Servicemodel.Web and then add using System.Runtime.Serialization.Json; at the top in order to fix the problems with DataContractJsonSerializer.  Next add using System.Collections.ObjectModel; at the top to fix the ObservableCollection squigglies.  Give your solution a quick Rebuild to ensure that everything compiles.

In analyzing the new code I just had you add, you’ll notice the use of DataContractJsonSerializer which in this case is used to de-serialize JSON-encoded Customer objects that are downloading over the air.  It does this by casting them as ObservableCollection<Customer> which works because I had you copy that class from the AzureRestService project.  In a world of right-clicking to add a Service/Web Reference to consume SOAP web services, you never had to know what was going on under the covers to create an easy to use proxy.  You also never saw Visual Studio create hidden classes that looked just like the ones found on the server side so your client would have something to work with and so that intellisense worked.

The last important line of code you see is setting the Customers table of your in-memory database equal to the de-serialization of the OpenReadCompletedEventArgs.  I think we’re ready to try out this code.  If all goes well, you’ll have a bunch of downloaded Customer data stored in an in-memory database on Windows Phone 7.

Set a breakpoint at the declaration of the DataContractJsonSerializer, hit F5, and click the Get Customers button!

WP75

Success looks like the picture above!  I hovered my mouse over Database.Instance.Customers and revealed that I was now storing Customers with Cities, CustomerId’s, PostalCodes, RouteId’s and StateProvinces for all to see in Visual Studio’s amazing debugger.

You are now consuming wireless-friendly WCF REST + JSON Services from both Windows Server and Windows Azure with data coming from SQL Server/SQL Azure.  Windows Phone 7 is connected to tiny, fast, efficient services designed to thrive in unreliable/slow wireless coverage.  You now know how to serialize/de-serialize .NET objects as JSON and you’ve also created your own in-memory database.

So what’s next?  In my next article, I’ll show you how to query this new in-memory database using LINQ and I’ll show you how to save the data in your local tables to Isolated Storage so you can keep using your apps even when the network has disappeared.

Keep coding and I’ll see everyone at Tech Ed in Berlin!

-Rob

Sharing my knowledge and helping others never stops, so connect with me on my blog at http://robtiffany.com , follow me on Twitter at https://twitter.com/RobTiffany and on LinkedIn at https://www.linkedin.com/in/robtiffany

Sign Up for my Newsletter and get a FREE Chapter of “Mobile Strategies for Business!”

[mc4wp_form id=”5975″]

Windows Phone 7 Line of Business App Dev :: Building a WCF REST + JSON Service

Visual Studio

Let’s start at the server and work our way to the phone.  Anyone who knows me is probably aware that I come from a wireless background so I’m always obsessed with things like coverage, bandwidth and latency when it comes to building mobile systems.  The only thing I assume in mobile development is frequent network dropouts and 28.8 kb/s modem speeds.  Think Compuserve.  For device apps to be successful, they must pre-fetch the data they need and cache it offline so a user can keep working when the network is not around.  This is not typical SOA, calling Web Services on-demand to help drive your application.  If your connectivity is that perfect, then build a mobile web site.  Additionally, they must first save captured data locally and only try to upload it to the server when network connectivity is available.  Never get caught in the trap of assuming ubiquitous network availability.

Networking + Wire Protocols

If you’ve looked at the Networking in Silverlight for Windows Phone 7 documentation on MSDN, you might have noticed that a number of things are missing:

  • WCF RIA Services
  • WCF Data Services (OData)
  • Duplex Communication over HTTP
  • Sockets
  • UDP Multicast Client
  • NTLM Auth
  • RSS/ATOM Feeds
  • Custom Bindings
  • JSON Serialization

I decided to focus on the networking capabilities that did work out of the box so that I could continue to build large-scale mobile/wireless solutions for our customers.  We’ve got SOAP and REST + Basic Authentication.  That’s right, you can add a Service Reference to an ASMX or WCF Web Service and you can pass your credentials to IIS with Basic Auth as long as you remember to wrap that clear text with SSL.  What I’m truly interested in is using the smallest, most efficient wire protocol possible to traverse all those unreliable wireless networks.  I’ve looked at various ways to do something simple like return a short list of Customers to a Windows Phone 7 device.  Some wire protocols are more efficient than others.  For instance:

  • An OData list of just 8 Customers from a WCF Data Service was extremely verbose and weighed in at a whopping 8.5 kb.  Yikes!

<?xml version=”1.0″ encoding=”utf-8″ standalone=”yes” ?>
– <feed xml:base=”
http://localhost:33779/WcfDataService1.svc/” xmlns:d=”http://schemas.microsoft.com/ado/2007/08/dataservices” xmlns:m=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata” xmlns=”http://www.w3.org/2005/Atom”>
<title type=”text”>Drivers</title>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers</id>
<updated>2010-05-24T22:12:38Z</updated>
<link rel=”self” title=”Drivers” href=”Drivers” />
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(1)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(1)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(1)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(1)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>1</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>1</d:DistributionCenterId>
<d:FirstName>Rob</d:FirstName>
<d:LastName>Tiffany</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(2)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(2)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(2)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(2)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>2</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>1</d:DistributionCenterId>
<d:FirstName>Loke Uei</d:FirstName>
<d:LastName>Tan</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(3)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(3)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(3)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(3)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>3</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>1</d:DistributionCenterId>
<d:FirstName>Dan</d:FirstName>
<d:LastName>Bouie</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(4)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(4)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(4)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(4)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>4</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>1</d:DistributionCenterId>
<d:FirstName>John</d:FirstName>
<d:LastName>Dietz</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(5)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(5)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(5)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(5)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>5</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>2</d:DistributionCenterId>
<d:FirstName>Derek</d:FirstName>
<d:LastName>Snyder</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(6)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(6)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(6)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(6)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>6</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>2</d:DistributionCenterId>
<d:FirstName>Steve</d:FirstName>
<d:LastName>Hegenderfer</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(7)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(7)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(7)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(7)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>7</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>2</d:DistributionCenterId>
<d:FirstName>Chip</d:FirstName>
<d:LastName>Vollers</d:LastName>
</m:properties>
</content>
</entry>
– <entry>
<id>
http://localhost:33779/WcfDataService1.svc/Drivers(8)</id>
<title type=”text” />
<updated>2010-05-24T22:12:38Z</updated>
– <author>
<name />
</author>
<link rel=”edit” title=”Driver” href=”Drivers(8)” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/DistributionCenter” type=”application/atom+xml;type=entry” title=”DistributionCenter” href=”Drivers(8)/DistributionCenter” />
<link rel=”
http://schemas.microsoft.com/ado/2007/08/dataservices/related/Todays” type=”application/atom+xml;type=feed” title=”Todays” href=”Drivers(8)/Todays” />
<category term=”ContosoBottlingModel.Driver” scheme=”
http://schemas.microsoft.com/ado/2007/08/dataservices/scheme” />
– <content type=”application/xml”>
– <m:properties>
<d:DriverId m:type=”Edm.Int32″>8</d:DriverId>
<d:DistributionCenterId m:type=”Edm.Int32″>2</d:DistributionCenterId>
<d:FirstName>James</d:FirstName>
<d:LastName>Pratt</d:LastName>
</m:properties>
</content>
</entry>
</feed>

  • The same list of 8 Customers using SOAP and a DataSet dropped us down to 3 kb but still too big for my taste.

<?xml version=”1.0″ encoding=”utf-8″?>
<DataSet xmlns=”
http://tempuri.org/”>
<xs:schema id=”NewDataSet” xmlns=”” xmlns:xs=”
http://www.w3.org/2001/XMLSchema” xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata”>
<xs:element name=”NewDataSet” msdata:IsDataSet=”true” msdata:UseCurrentLocale=”true”>
<xs:complexType>
<xs:choice minOccurs=”0″ maxOccurs=”unbounded”>
<xs:element name=”Driver”>
<xs:complexType>
<xs:sequence>
<xs:element name=”DriverId” type=”xs:int” minOccurs=”0″ />
<xs:element name=”DistributionCenterId” type=”xs:int” minOccurs=”0″ />
<xs:element name=”FirstName” type=”xs:string” minOccurs=”0″ />
<xs:element name=”LastName” type=”xs:string” minOccurs=”0″ />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata” xmlns:diffgr=”urn:schemas-microsoft-com:xml-diffgram-v1″>
<NewDataSet xmlns=””>
<Driver diffgr:id=”Driver1″ msdata:rowOrder=”0″>
<DriverId>1</DriverId>
<DistributionCenterId>1</DistributionCenterId>
<FirstName>Rob</FirstName>
<LastName>Tiffany</LastName>
</Driver>
<Driver diffgr:id=”Driver2″ msdata:rowOrder=”1″>
<DriverId>2</DriverId>
<DistributionCenterId>1</DistributionCenterId>
<FirstName>Loke Uei</FirstName>
<LastName>Tan</LastName>
</Driver>
<Driver diffgr:id=”Driver3″ msdata:rowOrder=”2″>
<DriverId>3</DriverId>
<DistributionCenterId>1</DistributionCenterId>
<FirstName>Dan</FirstName>
<LastName>Bouie</LastName>
</Driver>
<Driver diffgr:id=”Driver4″ msdata:rowOrder=”3″>
<DriverId>4</DriverId>
<DistributionCenterId>1</DistributionCenterId>
<FirstName>John</FirstName>
<LastName>Dietz</LastName>
</Driver>
<Driver diffgr:id=”Driver5″ msdata:rowOrder=”4″>
<DriverId>5</DriverId>
<DistributionCenterId>2</DistributionCenterId>
<FirstName>Derek</FirstName>
<LastName>Snyder</LastName>
</Driver>
<Driver diffgr:id=”Driver6″ msdata:rowOrder=”5″>
<DriverId>6</DriverId>
<DistributionCenterId>2</DistributionCenterId>
<FirstName>Steve</FirstName>
<LastName>Hegenderfer</LastName>
</Driver>
<Driver diffgr:id=”Driver7″ msdata:rowOrder=”6″>
<DriverId>7</DriverId>
<DistributionCenterId>2</DistributionCenterId>
<FirstName>Chip</FirstName>
<LastName>Vollers</LastName>
</Driver>
<Driver diffgr:id=”Driver8″ msdata:rowOrder=”7″>
<DriverId>8</DriverId>
<DistributionCenterId>2</DistributionCenterId>
<FirstName>James</FirstName>
<LastName>Pratt</LastName>
</Driver>
</NewDataSet>
</diffgr:diffgram>
</DataSet>

  • Switching to REST + XML dropped us to a much leaner 1.2 kb.

<ArrayOfDriver xmlns=http://schemas.datacontract.org/2004/07/ContosoWcfService.Models

xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”>
<Driver>
<DistributionCenterId>1</DistributionCenterId>
<DriverId>1</DriverId>
<FirstName>Rob</FirstName>
<LastName>Tiffany</LastName>
</Driver>
<Driver>
<DistributionCenterId>1</DistributionCenterId>
<DriverId>2</DriverId>
<FirstName>Loke Uei</FirstName>
<LastName>Tan</LastName>
</Driver>
<Driver>
<DistributionCenterId>1</DistributionCenterId>
<DriverId>3</DriverId>
<FirstName>Dan</FirstName>
<LastName>Bouie</LastName>
</Driver>
<Driver>
<DistributionCenterId>1</DistributionCenterId>
<DriverId>4</DriverId>
<FirstName>John</FirstName>
<LastName>Dietz</LastName>
</Driver>
<Driver>
<DistributionCenterId>2</DistributionCenterId>
<DriverId>5</DriverId>
<FirstName>Derek</FirstName>
<LastName>Snyder</LastName>
</Driver>
<Driver>
<DistributionCenterId>2</DistributionCenterId>
<DriverId>6</DriverId>
<FirstName>Steve</FirstName>
<LastName>Hegenderfer</LastName>
</Driver>
<Driver>
<DistributionCenterId>2</DistributionCenterId>
<DriverId>7</DriverId>
<FirstName>Chip</FirstName>
<LastName>Vollers</LastName>
</Driver>
<Driver>
<DistributionCenterId>2</DistributionCenterId>
<DriverId>8</DriverId>
<FirstName>James</FirstName>
<LastName>Pratt</LastName>
</Driver>
</ArrayOfDriver>

  • Switching my REST service encoding from XML to JSON to us down to a mere 639 bytes.
    • [{“DistributionCenterId”:1,”DriverId”:1,”FirstName”:”Rob”,”LastName”:”Tiffany”},{“DistributionCenterId”:1,”DriverId”:2,”FirstName”:”Loke Uei”,”LastName”:”Tan”},{“DistributionCenterId”:1,”DriverId”:3,”FirstName”:”Dan”,”LastName”:”Bouie”},{“DistributionCenterId”:1,”DriverId”:4,”FirstName”:”John”,”LastName”:”Dietz”},{“DistributionCenterId”:2,”DriverId”:5,”FirstName”:”Derek”,”LastName”:”Snyder”},{“DistributionCenterId”:2,”DriverId”:6,”FirstName”:”Steve”,”LastName”:”Hegenderfer”},{“DistributionCenterId”:2,”DriverId”:7,”FirstName”:”Chip”,”LastName”:”Vollers”},{“DistributionCenterId”:2,”DriverId”:8,”FirstName”:”James”,”LastName”:”Pratt”}]

 

Based on the title of this article, it’s probably obvious that I decided to go with REST + JSON because my customers care about speed, efficiency and reduced data costs.  I know I said JSON Serialization isn’t supported out of the box but that’s only partially true since you get the DataContractJsonSerializer to work with WCF objects encoded with JSON.  In this article, I’m going to walk you through the WCF project I built in Visual Studio 2010 so let’s get started.

Models

Since we won’t be generating a client-side proxy for your phone via adding a Service Reference or using slsvcutil.exe, then you can’t count on getting auto-generated Model objects.  This means you actually get to write some more code and build them yourself.  The Models you build on the server for your WCF Service will be the same one’s you copy to your Windows Phone 7 project so you’re client code will know what to do with them when they arrive. In order to ensure that your class and it’s properties are properly serialized for transport over the air, don’t forget to use the DataContract() and DataMember() attributes to make the magic happen.  Since I want my models to be Silverlight-friendly when it comes to data binding, I implement the INotifyPropertyChanged Interface.  While this may not seem important for the business entities you’re creating on the server, your Silverlight client needs this so I’m doing it now since I will be copying these same classes to my Windows Phone 7 project later.  The net of all this is that you add a PropertyChangedEventHandler to each class and you fire that event in each property’s setter.  This facilitates new or changed data in a bound control to update the model automatically.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace ContosoWcfService.Models
{
[DataContract()]
public class Customer : INotifyPropertyChanged
{
public Customer() { }

        private int customerId;
private int distributionCenterId;
private int routeId;
private string name;
private string streetAddress;
private string city;
private string stateProvince;
private string postalCode;

        [DataMember()]
public int CustomerId
{
get { return customerId; }
set
{
customerId = value;
NotifyPropertyChanged(“CustomerId”);
}
}

        [DataMember()]
public int DistributionCenterId
{
get { return distributionCenterId; }
set
{
distributionCenterId = value;
NotifyPropertyChanged(“DistributionCenterId”);
}
}

        [DataMember()]
public int RouteId
{
get { return routeId; }
set
{
routeId = value;
NotifyPropertyChanged(“RouteId”);
}
}

        [DataMember()]
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged(“Name”);
}
}

        [DataMember()]
public string StreetAddress
{
get { return streetAddress; }
set
{
streetAddress = value;
NotifyPropertyChanged(“StreetAddress”);
}
}

        [DataMember()]
public string City
{
get { return city; }
set
{
city = value;
NotifyPropertyChanged(“City”);
}
}

        [DataMember()]
public string StateProvince
{
get { return stateProvince; }
set
{
stateProvince = value;
NotifyPropertyChanged(“StateProvince”);
}
}

        [DataMember()]
public string PostalCode
{
get { return postalCode; }
set
{
postalCode = value;
NotifyPropertyChanged(“PostalCode”);
}
}

        public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

    }
}

Contract

In WCF, it’s all about contract-first development so you have to start out with a public Interface.  This is your Service Contract that exposes one or more Operation Contracts.  Since we’re going with REST, and I’m retrieving data, this means I’m going to use a GET request.  The WebGet attribute allows me to specify this easily.  Had I been Inserting, Updating or Deleting data, the WebInvoke attribute would’ve helped me with my POST, PUT or DELETE.  Next up is the use of the UriTemplate.  This allows me to point to REST/SEO friendly URIs to perform my operations rather than calling web methods that are buried inside a single URI.  In this case, I just want to retrieve a list of Customers so I set the UriTemplate = “/Customers.”  Since we’re trying to avoid the kind of extra baggage you get with SOAP, I set the BodyStyle == Bare which prevents extra XML wrapping of our parameters and return values.  For my ResponseFormat property, I set the WebMessageFormat to Json instead of XML in order to send the absolute minimum amount of data over the air.  Last but not least, I finish things up with my GetCustomers() method that returns an ObservableCollection of Customer objects to the Windows Phone 7 client.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using ContosoWcfService.Models;
using System.Collections.ObjectModel;

namespace ContosoWcfService
{
[ServiceContract]
public interface IRestData
{
[OperationContract]
[WebGet(UriTemplate = “/Customers”, BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
ObservableCollection<Customer> GetCustomers();

    }

}

Service Code

Now it’s time to implement my IRestData Interface with the RestData class.  Inside this class, I have my GetCustomers() method which connects to SQL Server, retrieves a list of Customers and sends it back to Windows Phone 7.  As you can see in the code below, I’m not using the Entity Framework but rather I’m just querying SQL Server directly.  Normally, I would call a stored procedure for even better performance but for clarity I’m showing the actual SELECT statement.  The key to this method is how I create new Customer objects and hydrate them with data as I loop through the SqlDataReader.  I then add them to the ObservableCollection which gets returned to the phone with wireless-friendly JSON encoding.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

using ContosoWcfService.Models;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Collections.ObjectModel;

namespace ContosoWcfService
{
public class RestData : IRestData
{
//Get the Database Connection string
private string _connectionString = WebConfigurationManager.ConnectionStrings[“ContosoBottlingConnectionString”].ConnectionString;

        public ObservableCollection<Customer> GetCustomers()
{
SqlConnection _cn = new SqlConnection(_connectionString);
SqlCommand _cmd = new SqlCommand();
_cmd.CommandText = “SELECT CustomerId, DistributionCenterId, RouteId, Name, StreetAddress, City, StateProvince, PostalCode FROM Customer”;

            try
{
_cn.Open();
_cmd.Connection = _cn;

                ObservableCollection<Customer> _customerList = new ObservableCollection<Customer>();

                SqlDataReader _dr = _cmd.ExecuteReader();
while (_dr.Read())
{
Customer _customer = new Customer();
_customer.CustomerId = Convert.ToInt32(_dr[“CustomerId”]);
_customer.DistributionCenterId = Convert.ToInt32(_dr[“DistributionCenterId”]);
_customer.RouteId = Convert.ToInt32(_dr[“RouteId”]);
_customer.Name = Convert.ToString(_dr[“Name”]);
_customer.StreetAddress = Convert.ToString(_dr[“StreetAddress”]);
_customer.City = Convert.ToString(_dr[“City”]);
_customer.StateProvince = Convert.ToString(_dr[“StateProvince”]);
_customer.PostalCode = Convert.ToString(_dr[“PostalCode”]);

                    //Add to List
_customerList.Add(_customer);
}
return _customerList;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
_cmd.Dispose();
_cn.Close();
}
}

Web.config

A WCF Service just wouldn’t be complete without a Web.config file.  The big takeway here is I’m storing my connection string to SQL Server and I’ve created a webHttp endpoint behavior for my REST services.  Down in my service name I point to my endpoint behavior, set my binding == webHttpBinding, and set my contract == IRestData.

<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>

  <system.web>
<compilation debug=”true” targetFramework=”4.0″ />
</system.web>

  <connectionStrings>
<add name=”ContosoBottlingConnectionString” connectionString=”Data Source=RTIFFANY3\SQLEXPRESS;Initial Catalog=ContosoBottling;Integrated Security=True” providerName=”System.Data.SqlClient” />
</connectionStrings>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>
<serviceMetadata httpGetEnabled=”true” />
<!– To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information –>
<serviceDebug includeExceptionDetailInFaults=”true” />
</behavior>
</serviceBehaviors>

      <endpointBehaviors>
<behavior name=”REST”>
<webHttp />
</behavior>

        <behavior name=”HelpBehavior”>
<webHttp helpEnabled=”true” />
</behavior>

      </endpointBehaviors>

    </behaviors>
<services>
<service name=”ContosoWcfService.RestData”>
<endpoint address=”” behaviorConfiguration=”REST” binding=”webHttpBinding” contract=”ContosoWcfService.IRestData” />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled=”true” />

  </system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests=”true” />
<directoryBrowse enabled=”true” />
</system.webServer>
</configuration>

Conclusion

I hope this helps get you started down the road to building wireless bandwidth efficient WCF Services with REST and JSON.  Keep in mind that you can build these same types of services in Windows Azure as well along with connections to SQL Azure.  In the next article, I’ll show you how to consume these types of services from your Windows Phone 7 project

Keep coding,

Rob

Sharing my knowledge and helping others never stops, so connect with me on my blog at http://robtiffany.com , follow me on Twitter at https://twitter.com/RobTiffany and on LinkedIn at https://www.linkedin.com/in/robtiffany

Sign Up for my Newsletter and get a FREE Chapter of “Mobile Strategies for Business!”

[mc4wp_form id=”5975″]