Telerik blogs

While there is much work yet to be done on Conference Buddy on the client side, it isn’t too early to start thinking about how we’ll store data on a server. One option that might be ideal would be to use ASP.NET Web API.

ASP.NET Web API is a framework for building HTTP services. ASP.NET Web API has been designed to work well with RESTful (where REST is an acronym for REpresentational State Transfer) design principles.

Fortunately, ASP.NET Web API guides you into creating services that follow a RESTful style. We will follow that style as long as it is useful. To see this at work, create a new application, and in the templates select ASP.NET MVC4 from the templates

New Project

After entering the name ContactManager, click OK. In the next dialog select the Web API Template.

ASP.NET MVC takes great advantage of “convention over configuration” meaning that if you place files in the expected folders, and use the expected naming conventions, a great deal of work is done for you.

Creating the Model

The first step is to create our Contact class. For this example, we’ll simplify Conference Buddy and build a simple contact manager, and the Contact class will be the fundamental data that we’ll use on both the server and the client.

Right click on the Models folder that was created for you and add a Contact.cs class

Note: This example is based on material from Pro Windows 8 Development With XAML and C#, APress 2013, written by Jon Galloway and Jesse Liberty

 

public class Contact
{
    [ScaffoldColumn(false)]
    public int ContactId { get; set; }
    [Required]
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public string Email { get; set; }
    public string Twitter { get; set; }
}

 

The first data annotation, ScaffoldColumn, indicates that the ContactId is controlled by the application and should not be set externally. The Required attribute indicates that, at a minimum, a Contact must have a name.

Creating the Controller

We are ready to create a Controller, but to do so we must first Build the application so that the Contact Model class will be available.

After building, right-click on the Controllers folder and select Add / Controller. Set the name of the controller to ContactsController.

Drop down the Template list and select

API controller with read/write actions, using Entity Framework.

Drop down the Model class list. If it is empty, you neglected to build the application, so cancel this dialog and build. From the drop down, select Contact (ContactManager.Models).

Drop down the Data context class list and select New Data Context. Name the new context

ContactManagerContext,

Add Controller

Conventions in ASP.NET Web API

ASP.NET Web API (like ASP.NET MVC) uses conventions to simplify code while conforming to HTTP standards. As you can see, all of the controller action methods are named using HTTP verbs, and ASP.NET Web API will automatically route HTTP requests to the appropriate method based on name. Thus an HTTP GET request to /api/contacts will automatically route to the ContactsController's GetContacts method, simply by following naming conventions.

Entity Framework and Sample Data

At this point, and with no further work, we have a functioning web service that uses Entity Framework to handle data persistence! Because this approach uses Entity Framework Code-First, it will automatically create our database – complete with tables based on our classes – when the service is first accessed.

Entity Framework also provides a method for inserting sample or initial data into our database when it's created, known as a database initializer.

Creating the Database Initializer

Right click on the Models folder and create a new class named ContactManagerDatabaseInitializer.

This class has one purpose – provide data when the database is initialized. Here's the code for that class:

public class ContactManagerDatabaseInitializer :
     DropCreateDatabaseIfModelChanges<ContactManagerContext>
 {
     protected override void Seed( ContactManagerContext context )
     {
         base.Seed( context );
 
         context.Contacts.Add(
             new Contact
             {
                 Name = "Jon Galloway",
                 Email = "jongalloway@gmail.com",
                 Twitter = "jongalloway",
                 City = "San Diego",
                 State = "CA"
             } );
 
         context.Contacts.Add(
             new Contact
             {
                 Name = "Jesse Liberty",
                 Email = "jesse.liberty@telerik.com",
                 Twitter = "jesseliberty",
                 City = "Acton",
                 State = "MA"
             } );
     }
 }

The ContactManagerContext was created for you and is used when overriding the Seed method (as we do above). When you register the initializer (see below) the initializer will be called by Entity Framework when it creates the database, and it will call your overridden seed method.

The context Contacts collection that you are adding to above is of type DbSet<Contact> which was created for you when you selected to create a new Data Context

Registering the Database Initializer

We need to register this database initializer in Global.asax.cs. We do so in the Application_Start method

Database.SetInitializer(new ContactManagerDatabaseInitializer());

You now have a server ready to provide data to a Windows 8 client.

Creating the Windows 8 Client

Right click on the solution and choose Add New Project. Select Windows Store in the left column, and Grid App in the right column. Name your application ContactManager.WindowsStore (once this technique is fully understood we’ll use Conference Buddy as the client side application).

Adding the WebApi Client package from NuGet

We’ll need the WebApi client package to work with the WebAPI application we’ve built. Fortunately, this can be installed through NuGet. To do this, select View->Other Windows ->Package Manager Console. This window will open at the bottom of your screen. At the Package Manager prompt (PM>) enter the following command:

Install-Package Microsoft.AspNet.WebApi.Client 
   -ProjectName ContactManager.WindowsStore –IncludePrerelease

Hit Enter and the package will be installed,

NuGet

Adding the Contact Class

We need to share the Contact class in the Server class as well as in the client. Copy and paste the class into the client project (yuck, but simpler for this example).

Right click on the DataModel folder and create a new class named Contact. Paste in the Contact class from ContactManager, eliminating the attributes,

public class Contact
{
    public int ContactId { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public string Email { get; set; }
    public string Twitter { get; set; }
}

Editing The SampleDataSource class

Within the ContactManager.WindowsStore project, open the SampleDataSource.cs file in the DataModel folder. Add the following functions to the end of the SampleDataSource class:

public static async Task<SampleDataGroup>
      LoadDataAsync( bool clear = true )
        {
            // Load this from configuration
            const string serviceUrl = "http://localhost:16854/";
 
            if ( clear )
            {
                _sampleDataSource.AllGroups.Clear();
            }
 
            HttpClient client = new HttpClient();
 
            client.BaseAddress = new Uri( serviceUrl );
            HttpResponseMessage response = await client.GetAsync( "api/contacts" );
            Contact[] contacts = await response.Content.ReadAsAsync<Contact[]>();
 
            SampleDataGroup contactsGroup = 
                    new SampleDataGroup( "ContactsGroup", "My Contacts", null, null, null );
 
            foreach ( Contact contact in contacts )
            {
                contactsGroup.Items.Add( new SampleDataItem(
                    contact.ContactId.ToString(),
                    contact.Name,
                    GetContactInfo( contact ),
                    //new Uri(client.BaseAddress, contact.Self + ".png").AbsoluteUri,
                    new Uri( "http://avatars.io/auto/" + contact.Twitter + "?size=large", 
                                              UriKind.Absolute ).AbsoluteUri,
                    null,
                    null,
                    contactsGroup ) );
            }
            _sampleDataSource.AllGroups.Add( contactsGroup );
 
            return contactsGroup;
        }
 
 static string GetContactInfo( Contact contact )
        {
            return string.Format(
                "{0}, {1}\n{2}\n@{3}",
                contact.City ?? "City?",
                contact.State ?? "State?",
                contact.Email ?? "Email?",
                contact.Twitter ?? "Twitter?" );
        }

Take a close look at the first function, LoadDataAsync:

const string serviceUrl = "http://localhost:16854/";

This function will be calling into our service. In the current example application, this will need to match the port number used by the ASP.NET Web API application. To find this, scroll up to the ContactManager project and double click on the Properties

Properties

When the Properties display opens, click on Web (in the left column) to open the Web tab. About half way down the page you’ll find the Servers section. Within that, find the Project Url for the Local IIS Web Server and note the Port Number.

Change the port number in your code accordingly,

 

public static async Task<SampleDataGroup> LoadDataAsync( bool clear = true )
        {
            // Load this from configuration
            const string serviceUrl = "http://localhost:36899/";

If the Boolean value clear evaluates to true, we clear the data source before fetching data from the web service. In this case, we do want to have sample data available for design-time support, but we don't want to have the sample data flash on the screen when the application runs, so we'll leave the default to true.

We then instantiate an HttpClient object and set its BaseAddress. We use that to get the contacts asynchronously. They are returned as JSON data that we can then read into a collection of Contact objects.

HttpClient client = new HttpClient();
 
    client.BaseAddress = new Uri(serviceUrl);
    HttpResponseMessage response = await client.GetAsync("api/contacts");
    Contact[] contacts = await response.Content.ReadAsAsync<Contact[]>();

Surprisingly, this is all you need to do to call a webservice which returns JSON data. The HttpClient object will make the request, wait asynchronously for the response, and deserialize the result to an array of Contact objects.

SampleDataGroup contactsGroup = 
                     new SampleDataGroup("ContactsGroup", "My Contacts", null, null, null);
 
foreach (Contact contact in contacts)
{
    contactsGroup.Items.Add(new SampleDataItem(
        contact.ContactId.ToString(),
        contact.Name,
        GetContactInfo(contact),
        //new Uri(client.BaseAddress, contact.Self + ".png").AbsoluteUri,
        new Uri("http://avatars.io/auto/" + contact.Twitter + 
                                          "?size=large", UriKind.Absolute).AbsoluteUri,
        null,
        null,
        contactsGroup));
}
_sampleDataSource.AllGroups.Add(contactsGroup);
 
return contactsGroup;

The SampleDataSource can hold separate groups of data. We're returning all contacts in an AllGroups grouping, but we could split these up if we had a meaningful way to do that – by geographical region, for instance. We'd add those groups to the _sampleDataSource.

Notice that the third parameter in the SampleDataItem constructor is GetContactInfo which takes the contact as a parameter. This returns a string based on what information is available for the Contact,

static string GetContactInfo( Contact contact )
{
    return string.Format(
        "{0}, {1}\n{2}\n@{3}",
        contact.City ?? "City?",
        contact.State ?? "State?",
        contact.Email ?? "Email?",
        contact.Twitter ?? "Twitter?" );
}

This is the data that will be displayed on the Group and on the Items page. That data is displayed automatically as we’ve passed that string in as the third parameter to the SampleDataItem, and that third parameter is the Sub-title.

Key to understanding how this works is that we are not changing any of the out of the box behavior of the Grid App template; we are simply providing alternative data to the built-in sample data, mapping our contact data to the expected strings.

Avatars

We're using another web service for our avatars, avatars.io. This site takes a handle and tries to find the best avatar image from popular social networking sites like Twitter and Facebook.

new Uri( "http://avatars.io/auto/" + contact.Twitter + 
           "?size=large", UriKind.Absolute ).AbsoluteUri

Calling LoadDataAsync

Finally, we'll need to call the LoadDataAsync method from App.xaml.cs. Add the line

SampleDataSource.LoadDataAsync();

above the call to Window.Current.Content.RootFrame,

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)
     {
          //…
 
         SampleDataSource.LoadDataAsync();
         // Place the frame in the current Window
         Window.Current.Content = rootFrame;
     }

Run the application and the Grid App page is displayed using the default display characteristics, but using our data from our Web Service, rather than the sample data.

Running Program

 

Download the Source Code for this demonstration


Win8_Download (2)


jesseLiberty
About the Author

Jesse Liberty

 has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and has been a Distinguished Software Engineer for AT&T and a VP for Information Services for Citibank and a Software Architect for PBS. You can read more on his personal blog or follow him on twitter

Comments

Comments are disabled in preview mode.