If you are writing Windows 8 app, chances are that you will need to communicate with some sort of service to retrieve or send data.
In this blog post I will show how to set up a basic CRUD ASP.NET Web API REST like service and how to consume that service from a C# Windows XAML 8 app ('modern style' aka 'M** style' that is :)). I will also show how to build a simple user interface in XAML powered by data retrieved from our service and how to leverage MVVM pattern to make the code a little bit more maintainable.
Let's start off by creating a simple model for our example.
public abstract class Entity { public Guid Id { get; set; } }
public class Expense : Entity { public string Name { get; set; } public DateTime Date { get; set; } public decimal Amount { get; set; } public string Type { get; set; } public string Notes { get; set; } public string Account { get; set; } }
We also want a way to persist our objects, instead of using a database I will just store them in memory (using thread safe collections introduced in .NET 4) and use a repository pattern to abstract actual persistence mechanism.
public interface IRepository<TEntity> where TEntity : Entity { TEntity Add(TEntity entity); TEntity Delete(Guid id); TEntity Get(Guid id); TEntity Update(TEntity entity); IQueryable<TEntity> Items { get; } }
CRUD service in Web API
The next step is to create a simple Web API service that will provide basic CRUD operations.
HTTP/1.1 protocol defines a set of common methods that map to these operations in a following way:
- GET /api/expenses - get a list of all expenses
- GET /api/expenses/id - get expense by id
- POST /api/expenses - create a new expense
- PUT /api/expenses/id - update an expense
- DELETE /api/expenses/id - delete an expense
If you have been implementing REST services before it may seem to be quite obvious.
The tricky part is (at least in my opinion) to make our service as HTTP compliant as possible by returning appropriate responses and status codes.
public class ExpensesController : ApiController { public static IRepository<Expense> ExpenseRepository = new InMemoryRepository<Expense>(); public IEnumerable<Expense> Get() { var result = ExpenseRepository.Items.ToArray(); return result; } public Expense Get(Guid id) { Expense entity = ExpenseRepository.Get(id); if(entity == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return entity; } public HttpResponseMessage Post(Expense value) { var result = ExpenseRepository.Add(value); if(result == null) { // the entity with this key already exists throw new HttpResponseException(HttpStatusCode.Conflict); } var response = Request.CreateResponse<Expense>(HttpStatusCode.Created, value); string uri = Url.Link("DefaultApi", new { id = value.Id }); response.Headers.Location = new Uri(uri); return response; } public HttpResponseMessage Put(Guid id, Expense value) { value.Id = id; var result = ExpenseRepository.Update(value); if(result == null) { // entity does not exist throw new HttpResponseException(HttpStatusCode.NotFound); } return Request.CreateResponse(HttpStatusCode.NoContent); } public HttpResponseMessage Delete(Guid id) { var result = ExpenseRepository.Delete(id); if(result == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return Request.CreateResponse(HttpStatusCode.NoContent); } }
You can find HTTP/1.1 spec document here (cough@frystyk is a co-author of this document cough). For example a section on POST method states that for resources that can be identified by a URI "the response SHOULD be 201 (Created) and contain an entity which describes the status of the request (...) and [contain] a Location header". Because expenses can be identified by a URL /api/expenses/id (it is a resource), ideally we should follow this specification. Fortunately HTTP is a first class citizen in Web API and tasks like setting Location header in response are simple. In real world scenario we would also get rid of an unbounded Get() method and use paging or other result limiting mechanism.
Windows 8 app
For the sake of demonstration I am keeping the application very simple (and ugly unfortunately). It will consist of one screen and will be capable of:
- getting and displaying a list of all expenses,
- adding a new expense (with randomly generated properties),
- deleting selected expense,
- modifying selected expense.
All these operations will call our new shiny Web API HTTP service to retrieve and manipulate data.
Communicating with Web API
To consume HTTP service from Windows 8 app we can use an instance of HttpClient or HttpWebRequest class (the latter provides a little bit more features) . It's worth mentioning that out of the box, ASP.NET Web API supports both xml and json as media types for messages being exchanged with clients (you can use accept header to specify which one you want to use). Windows 8 also supports serialization/deserialization for both formats (through XmlSerializer and DataContractJsonSerializer classes respectively), without a need for external libraries. In this app I am going to use HttpClient and json as a message format.
But where should we put the logic to actually communicate with our server and serialize/deserialize entities?
The simplest solution would be to place it directly in code behind in event handlers associated with particular user actions. Even though suitable for such a simple demo app, this approach would backfire on us in more complex scenarios (imagine for example that we wanted to add authorization to our service in the future - we would need update code in many places). Because of this lets abstract actual data manipulation (and communication) logic.
public interface IExpenseService { Task<IEnumerable<Expense>> GetAll(); Task Add(Expense expense); Task Delete(Guid id); Task Update(Expense expense); }
This interface provides basic data manipulation operations, its implementation that uses Json (DataContractJsonSerializer) and HTTP to communicate with Web API service will look like this:
public class ExpenseService : IExpenseService { private const string ServiceUrl = "http://localhost:12898/api/expenses"; private readonly HttpClient _client = new HttpClient(); public async Task<IEnumerable<Expense>> GetAll() { HttpResponseMessage response = await _client.GetAsync(ServiceUrl); var jsonSerializer = CreateDataContractJsonSerializer(typeof(Expense[])); var stream = await response.Content.ReadAsStreamAsync(); return (Expense[])jsonSerializer.ReadObject(stream); } public async Task Add(Expense expense) { var jsonString = Serialize(expense); var content = new StringContent(jsonString, Encoding.UTF8, "application/json"); var result = await _client.PostAsync(ServiceUrl, content); } public async Task Delete(Guid id) { var result = await _client.DeleteAsync(String.Format("{0}/{1}" , ServiceUrl, id.ToString())); } public async Task Update(Expense expense) { var jsonString = Serialize(expense); var content = new StringContent(jsonString, Encoding.UTF8, "application/json"); var result = await _client.PutAsync(String .Format("{0}/{1}", ServiceUrl, expense.Id), content); } private static DataContractJsonSerializer CreateDataContractJsonSerializer(Type type) { const string dateFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ"; var settings = new DataContractJsonSerializerSettings { DateTimeFormat = new DateTimeFormat(dateFormat) }; var serializer = new DataContractJsonSerializer(type, settings); return serializer; } private string Serialize(Expense expense) { var jsonSerializer = CreateDataContractJsonSerializer(typeof(Expense)); byte[] streamArray = null; using (var memoryStream = new MemoryStream()) { jsonSerializer.WriteObject(memoryStream, expense); streamArray = memoryStream.ToArray(); } string json = Encoding.UTF8.GetString(streamArray, 0, streamArray.Length); return json; } }
The main benefit of DataContractJsonSerializer is that it is being provided by the platform (no concerns about using 3rd party library). On the other hand it requires a little bit of code to set up and its api is a somewhat clunky.
Here is IExpenseService implementation that uses Json.NET, which is a very popular Json serialization library for .NET. If you want to use it the easiest way is to get a NuGet package.
public class JsonNetExpenseService : IExpenseService { private const string ServiceUrl = "http://localhost:12898/api/expenses"; private readonly HttpClient _client = new HttpClient(); public async Task<IEnumerable<Expense>> GetAll() { HttpResponseMessage response = await _client.GetAsync(ServiceUrl); var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<Expense[]>(jsonString); } public async Task Add(Expense expense) { var jsonString = Serialize(expense); var content = new StringContent(jsonString, Encoding.UTF8, "application/json"); var result = await _client.PostAsync(ServiceUrl, content); } public async Task Delete(Guid id) { var result = await _client.DeleteAsync(String.Format("{0}/{1}" , ServiceUrl, id.ToString())); } public async Task Update(Expense expense) { var jsonString = Serialize(expense); var content = new StringContent(jsonString, Encoding.UTF8, "application/json"); var result = await _client.PutAsync(String.Format("{0}/{1}", ServiceUrl, expense.Id), content); } private string Serialize(Expense expense) { return JsonConvert.SerializeObject(expense); } }
I like it better :).
Please note that we are leveraging async/await support provided by the platform. It will help us reduce code complexity later on.
XAML
IExpenseService enables us to communicate with our Web API service, but we need to call this logic somewhere and we obviously need UI as well. As I said before, to keep things simple lets use one page only.
<Page> <Page.BottomAppBar> <AppBar IsSticky="True" IsOpen="True"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal"> <Button Style="{StaticResource RefreshAppBarButtonStyle}" AutomationProperties.Name="Refresh Data" Command="{Binding RemoveCommand}"/> <Button Style="{StaticResource AddAppBarButtonStyle}" AutomationProperties.Name="Add Item" Command="{Binding AddCommand}"/> </StackPanel> <StackPanel Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal"> <Button Style="{StaticResource EditAppBarButtonStyle}" AutomationProperties.Name="Update Selected Item" Command="{Binding UpdateCommand}"/> <Button Style="{StaticResource DeleteAppBarButtonStyle}" AutomationProperties.Name="Delete Selected Item" Command="{Binding DeleteCommand}"/> </StackPanel> </Grid> </AppBar> </Page.BottomAppBar> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <ListView SelectedItem="{Binding SelectedItem, Mode=TwoWay}" ItemsSource="{Binding Items}"> <ListView.ItemTemplate> <DataTemplate> ... </DataTemplate> </ListView.ItemTemplate> </ListView> <ProgressRing IsActive="{Binding IsBusy}" Width="70" Height="70"/> </Grid> </Page>
Please note that I am using MVVM pattern, but if you dont feel comfortable with it you can just stick IExpenseService calls in code-behind. Expense list will be displayed by ListView. We use bottom AppBar to provide user with buttons and there is a simple activity indicator (ProgressRing) which will be displayed while we are retrieving data from the server.
View model
We also need to create a view model that the page will bind to and that will encapsulate view related logic.
public class MainViewModel : INotifyPropertyChanged { // ... private const string ServiceUrl = "http://localhost:12898/api/expenses"; public IExpenseService ExpenseService { get; set; } public Expense SelectedItem { get { return _selectedItem; } set { if (_selectedItem != value) { _selectedItem = value; DeleteCommand.RaiseCanExecuteChanged(); UpdateCommand.RaiseCanExecuteChanged(); OnPropertyChanged("SelectedItem"); } } } public bool IsBusy { // ... } public IEnumerable<Expense> Items { // ... } #region commands public RelayCommand AddCommand { get; set; } public RelayCommand RefreshCommand { get; set; } public RelayCommand DeleteCommand { get; set; } public RelayCommand UpdateCommand { get; set; } #endregion commands public MainViewModel() { CreateCommands(); ExpenseService = new ExpenseService(); } private void CreateCommands() { AddCommand = new RelayCommand(o => AddHandler()); RefreshCommand = new RelayCommand(o => RefreshHandler()); DeleteCommand = new RelayCommand(o => DeleteHandler(), () => SelectedItem != null); UpdateCommand = new RelayCommand(o => UpdateHandler(), () => SelectedItem != null); } private void UpdateHandler() { if(SelectedItem != null) { var random = new Random(); var amount = GenerateAmount(random); SelectedItem.Amount = amount; IsBusy = true; ExpenseService.Update(SelectedItem); IsBusy = false; RefreshData(); } } private async void DeleteHandler() { if(SelectedItem != null) { IsBusy = true; await ExpenseService.Delete(SelectedItem.Id); IsBusy = false; RefreshData(); } } private void RefreshHandler() { RefreshData(); } private async void AddHandler() { Random random = new Random(); int account = random.Next(1, 999); var amount = GenerateAmount(random); Expense newExpense = new Expense() { Account = account.ToString(), Date = DateTime.UtcNow, Amount = amount, Name = GenerateName(random), Notes = "Some notes", Type = GenerateType(random), }; IsBusy = true; await ExpenseService.Add(newExpense); IsBusy = false; RefreshData(); } // ... public void Load() { RefreshData(); } private async void RefreshData() { IsBusy = true; Items = await ExpenseService.GetAll(); IsBusy = false; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
I have removed less important code to make it smaller and easier to read. I am also generating random data instead of providing user editable forms.
There are two patterns worth mentioning here. First of all we are using commands (RelayCommand is an ICommand implementation and you can find it in source code) to react to user actions. For delete and update operations we provide canExecute predicate that determines whether a command can be executed or not. This is used to disable buttons when no item is selected.
The other pattern is the use of IsBusy property to indicate that view model is 'busy' - in our scenario that we are in progress of sending or retrieving data from server. Thanks to async/await support in .NET 4.5 and C# Windows 8 apps we don't have to worry about thread marshaling and the code looks as if it was synchronous.
You can download or view full source code (for both services and client app) on bitbucket (alternatively zip archive here).