Channels ▼
RSS

.NET

Access Data with REST in Windows 8 Apps


  1. Add a new Services folder.
  2. Add a new interface, IGroupService, within the Services folder. The following is the code for IGroupService:
  3. namespace DrDobbsApp.Services
    {
        using System.Collections.Generic;
        using System.Threading.Tasks;
    
        //// DrDobbsApp.Entities (Portable Class Library)
        using Entities;
    
        public interface IGroupService
        {
            Task<IEnumerable<Group>> GetAll();
        }
    }

  4. Create a subfolder named Implementation within the Services folder.
  5. Add a new class, GroupService, within Services\Implementation. The following code for GroupService, that implements the previously created interface:
  6. namespace DrDobbsApp.Services.Implementation
    {
        using System;
        using System.Collections.Generic;
        using System.Net.Http;
        using System.Net.Http.Headers;
        using System.Threading.Tasks;
        using System.Runtime.Serialization;
        using System.Runtime.Serialization.Json;
    
        //// DrDobbsApp.Entities (Portable Class Library)
        using Entities;
    
        public class GroupService : IGroupService
        {
            private const string RestServiceUrl = "http://localhost:46037/api/";
    
            public async Task<IEnumerable<Group>> GetAll()
            {
                var client = new HttpClient
                {
                    BaseAddress = new Uri(RestServiceUrl)
                };
                
                //// I want to add and accept a header for JSON
                client.DefaultRequestHeaders.Accept.Add(new 
    MediaTypeWithQualityHeaderValue(
    "application/json"));
    
                //// Retrieve all the groups with their items
                var response = await client.GetAsync("groups");
    
                //// Throw an exception if something went wrong
                response.EnsureSuccessStatusCode();
    
                //// In case you need date and time properties
                const string dateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
                var jsonSerializerSettings = new DataContractJsonSerializerSettings
                {
                    DateTimeFormat = new DateTimeFormat(dateTimeFormat)
                };
    
                var jsonSerializer = new DataContractJsonSerializer(
                    typeof(Group[]),
                    jsonSerializerSettings);
    
                var stream = await response.Content.ReadAsStreamAsync();
                return (Group[]) jsonSerializer.ReadObject(stream);
            }
        }
    }
    

The GroupService class has a GetAll method that uses the async modifier and returns a Task<IEnumerable<Group>>. As you already know, you can call this method with the await keyword and you will be able to retrieve an IEnumerable<Group> without having to worry about the Task wrapper.

The GetAll method performs the following steps:

  1. Creates a new instance of the System.Net.Http.HttpClient class and sets its base address to the ASP.NET Web API rest service URL.
  2. var client = new HttpClient
    {
        BaseAddress = new Uri(RestServiceUrl)
    };
    
  3. Adds and accepts a header for JSON for the HttpClient instance.
  4. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  5. Makes the REST call to GET api/groups with an asynchronous execution by using the HttpClient.GetAsync method. The method returns a Task<HttpResponseMessage> that the await keyword unwraps into an HttpResponseMessage.
  6. var response = await client.GetAsync("groups");
  7. Calls the EnsureSuccessStatusCode for the HttpResponseMessage instance. This method throws an exception if something goes wrong. Of course, more exception handling code should be added. However, I'm trying to keep things simple.
  8. response.EnsureSuccessStatusCode();
  9. Creates a new instance of DataContractJsonSerializerSettings and sets a value for the DateTimeFormat property. In this case, I haven't added DateTime values. However, I've added the code so that you have a baseline to deserialize JSON with REST calls in Windows Store apps.
  10. const string dateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
    var jsonSerializerSettings = new DataContractJsonSerializerSettings
    {
        DateTimeFormat = new DateTimeFormat(dateTimeFormat)
    };
    
  11. Creates a new DataContractJsonSerializer instance specifiying and array of Group as a type and the previously explained settings.
  12. var jsonSerializer = new DataContractJsonSerializer(
        typeof(Group[]),
        jsonSerializerSettings);
    
  13. Calls the ReadAsStreamAsync method for the Content property of the response that is an instance of HttpContent. The method returns Task<System.IO.Stream>. The code makes the call with an asynchronous execution by using the await keyword that unwraps the response into a System.IO.Stream instance.
  14. var stream = await response.Content.ReadAsStreamAsync();
  15. Finally, the code returns the results of the call to the ReadObject for the DataContractJSonSerializer instance that receives the previously read System.IO.Stream as a parameter. The code returns an array of Group (Group[]).
  16. return (Group[]) jsonSerializer.ReadObject(stream);</p>

It is necessary to make sure that the App manifest provides outbond access to the Internet to consume the REST service with HttpClient. If you double click on Package.appxmanifest and then select the Capabilities page, Visual Studio displays the different capabilities that determine the features and devices that your app can use. In this case, it is necessary to make sure Internet (Client) is checked (see Figure 10).


Figure 10: The system features or devices that the app can use with Internet (Client) as the only capability enabled for the app.

Making Asynchronous Calls to Replace the Sample Data Source

Now, I just need to replace the sample data source included in the Grid app template with the results of calling the previously created GetAll method that retrieves all the groups and their items from the REST service. The code isn't going to represent best practices because I'm reusing many existing pieces from the Grid app template and keeping most of the code of the SampleDataSource class, which is not the perfect implementation of a data source. However, you will be able to understand the idea and use them in your better designed Model-View-ViewModel Windows Store app. The following steps enable you to display the results of the GetAll method in the different views (also known as pages) of the Grid app template:

  1. Remove all the code in the SampleDataSource constructor. The constructor was adding the sample groups and items.
  2. Add a new static asynchronous method to the SampleDataSource class (RefreshDataSource):
  3. public static async Task RefreshDataSource()
    {
        var groupService = new GroupService();
        var allGroups = await groupService.GetAll();
        foreach (var group in allGroups)
        {
            var sampleDataGroup = new SampleDataGroup(
                group.Id,
                group.Title,
                group.Subtitle,
                "Assets/LightGray.png",
                group.Description);
    
            _sampleDataSource.AllGroups.Add(sampleDataGroup);
    
            if (group.Items != null)
            {
                foreach (var item in group.Items)
                {
                    sampleDataGroup.Items.Add(new SampleDataItem(
                        item.Id,
                        item.Title,
                        item.Subtitle,
                        "Assets/LightGray.png",
                        item.Description,
                        item.Content,
                        sampleDataGroup));
                }
            }
        }
    }
    
  4. Go to App class (App.xaml.cs) and change the code for the OnLaunched event handler so that it calls the previously created SampleDataSource.RefreshDataSource with an asynchronous execution when the app's previous state is ApplicationExecutionState.NotRunning.
  5. protected override async void OnLaunched(LaunchActivatedEventArgs args)
    {
        Frame rootFrame = Window.Current.Content as Frame;
    
        // Do not repeat app initialization when the Window already has content,
        // just ensure that the window is active
                
        if (rootFrame == null)
        {
            // Create a Frame to act as the navigation context, and navigate to first page
            rootFrame = new Frame();
            //Associate the frame with a SuspensionManager key                                
            SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
    
            if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                // Restore the saved session state only when appropriate
                try
                {
                    await SuspensionManager.RestoreAsync();
                }
                catch (SuspensionManagerException)
                {
                    //Something went wrong restoring state.
                    //Assume there is no state and continue
                }
            }
            else if (args.PreviousExecutionState == ApplicationExecutionState.NotRunning)
            {
                //// Retrieve all the groups by making a call to the REST service
                try
                {
                    await Data.SampleDataSource.RefreshDataSource();
                }
                catch (Exception ex)
                {
                    ////Something went wrong refreshing the data source
                    ////It is obviously necessary to add more code here :)
                }
            }
    
            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }
        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter
            if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
            {
                throw new Exception("Failed to create initial page");
            }
        }
        // Ensure the current window is active
        Window.Current.Activate();
    }
    

The SampleDataSource.RefreshDataSource method calls the GroupService.GetAll method with an asynchronous execution by using the await keyword. When GetAll returns all the groups with their items, the code creates the SampleDataGroup and SampleDataItem instances with the same images used in the original app template. I've reused SampleDataGroup and SampleDataItem to take advantage of the entire infrastructure provided the Grid app template and focus on just a few specific topics while providing a complete example. Of course, there is a clear need for a better exception handling in this method and in the rest of the app.

In addition, it would be necessary to take advantage of caching mechanisms and querying mechanisms to avoid retrieving the entire content each time the app is launched. In this case, I'm using the images provided by the Grid template to keep the example simple. However, in an app like this one, you might like to cache some images that won't change all the time, such as the image used for Dr. Dobb's Cloud section (a group in the app).

As explained in the previous article, the OnLaunched event handler method uses the async modifier and therefore is ready to make calls with the await keyword. The new code executes SampleDataSource.RefreshDataSource with an asynchronous execution when the previous state for the app is ApplicationExecutionState.NotRunning and leaves the data source ready to be displayed by the different pages.

If you run the application step-by-step, you will be able to understand the flow and the different asynchronous executions that allow the app to display the data retrieved from the REST service. The first view displays the groups (Figure 11).


Figure 11: The app displaying the groups (GroupedItemsPage).

If you click on Cloud, the app will display the details for the group with the two items, which are the two articles (see Figure 12).


Figure 12: The app displaying the details for the Cloud group (GroupDetailsPage).

If you click on an item, the app will display its details (see Figure 13). The app uses all the navigation features of the Grid app template but retrieving the groups and items from a REST service.


Figure 13: The app displaying the details for one of the items (ItemDetailsPage).

You can also test the app with the simulator included in Visual Studio 2012. You just need to select Simulator in the drop down menu located in the Standard toolbar (see Figure 14).


Figure 14: Selecting the Simulator as the target for the app in Visual Studio 2012.

The simulator allows you to see how the app behaves in a device. You can rotate the simulator (see Figure 15), set different resolutions, configure a desired location and use some touch emulation with the mouse, among other features.


Figure 15: The Grid app template redefines the layout when the device is rotated.

Conclusion

I've provided a full example of how to develop and consume a REST service that provides data to a Windows Store app that uses the Grid app, developed with C# and XAML. This solution uses a Portable Class Library to share code between the REST service, developed with the new ASP.NET Web API, and the Windows Store app. The app makes asynchronous calls to follow the best practices for Windows Store apps. Now, you have what you need to start working with Windows 8 apps that require a data source.

Related Reading

Writing Your First Windows 8 App: The Lay of the Land

Customizing the Appearance of Windows 8 Apps


Gaston Hillar is a frequent contributor to Dr. Dobb's on the topic of Microsoft development.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video