Creating an API Data Service

Using BaseHttpService as a foundation that abstracts away the HTTP request details, we can now begin to create services that leverage it to get responses back from the API in the form of domain-specific models. Specifically, we will create a data service that can be used by the ViewModels to get the TripLogEntry objects from the backend service.

We will start off by defining an interface for the data service that can be injected into the ViewModels, ensuring there is no strict dependency on the API or the logic that communicates with it, continuing the pattern we put in place in Chapter 4, Platform Specific Services and Dependency Injection.

  1. Create a new interface named ITripLogDataService in the Services folder in the core library:
    public interface ITripLogDataService
    { }
  2. Update the ITripLogDataService interface with methods to get, update, and delete TripLogEntry objects:
    public interface ITripLogDataService
    {
        Task<IList<TripLogEntry>> GetEntriesAsync ();
        Task<TripLogEntry> GetEntryAsync (string id);
        Task<TripLogEntry> AddEntryAsync (TripLogEntry entry);
        Task<TripLogEntry> UpdateEntryAsync (TripLogEntry entry);
        Task RemoveEntryAsync (TripLogEntry entry);
    }

    Next, we need to create an implementation of this interface that will also subclass BaseHttpService so that it has access to our HttpClient implementation.

  3. Create a new class named TripLogApiDataService in the Services folder in the core library:
    public class TripLogApiDataService
       : BaseHttpService, ITripLogDataService
    { }
  4. Add two private properties to the TripLogApiDataService class: a Uri and an IDictionary<string, string>, to store the base URL and headers respectively, to be used for all requests:
    public class TripLogApiDataService
       : BaseHttpService, ITripLogDataService
    {
        readonly Uri _baseUri;
        readonly IDictionary<string, string> _headers;
    }
  5. Update the TripLogApiDataService constructor to take in a Uri parameter and set the private _baseUri and _headers properties:
    public class TripLogApiDataService
       : BaseHttpService, ITripLogDataService
    {
        readonly Uri _baseUri;
        readonly IDictionary<string, string> _headers;
    
        public TripLogApiDataService (Uri baseUri)
        {
            _baseUri = baseUri;
            _headers = new Dictionary<string, string> ();
    
            // TODO: Add header with Authentication-based token in chapter 7
            _headers.Add ("zumo-api-version", "2.0.0");
        }
    }
  6. Finally, implement the members of ITripLogDataService using the SendRequestAsync<T> base class method:
    public class TripLogApiDataService
       : BaseHttpService, ITripLogDataService
    {
        readonly Uri _baseUri;
        readonly IDictionary<string, string> _headers;
    
        // ...
    
        #region ITripLogDataService implementation
    
        public async Task<IList<TripLogEntry>> GetEntriesAsync ()
        {
            var url = new Uri (_baseUri, "/tables/entry");
            var response = await SendRequestAsync<TripLogEntry[]> (url, HttpMethod.Get, _headers);
            return response;
        }
    
        public async Task<TripLogEntry> GetEntryAsync (string id)
        {
            var url = new Uri (_baseUri, string.Format ("/tables/entry/{0}", id));
            var response = await SendRequestAsync<TripLogEntry> (url, HttpMethod.Get, _headers);
            return response;
        }
    
        public async Task<TripLogEntry> AddEntryAsync (TripLogEntry entry)
        {
            var url = new Uri (_baseUri, "/tables/entry");
            var response = await SendRequestAsync<TripLogEntry> (url, HttpMethod.Post, _headers, entry);
            return response;
        }
    
        public async Task<TripLogEntry> UpdateEntryAsync (TripLogEntry entry)
        {
            var url = new Uri (_baseUri, string.Format ("/tables/entry/{0}", entry.Id));
            var response = await SendRequestAsync<TripLogEntry>(url, new HttpMethod("PATCH"), _headers, entry);
            return response;
        }
    
        public async Task RemoveEntryAsync (TripLogEntry entry)
        {
            var url = new Uri (_baseUri, string.Format ("/tables/entry/{0}", entry.Id));
            var response = await SendRequestAsync<TripLogEntry>(url, HttpMethod.Delete, _headers);
        }
    
        #endregion
    }

Each method in this TripLog service calls the SendRequestAsync method on the base class passing in the API route, the appropriate HttpMethod, and the zumo-api-version header that we used in the first section. The Add and Update methods also pass in a TripLogEntry object that will be serialized and added to the HTTP request message content. In the next chapter, we will implement authentication with the API and update this service to pass in an authentication based token in the header.

Updating the TripLog app ViewModels

Using the API and data service we created, we can now update the ViewModels in the app to use live data instead of the local, hard-coded data they currently use. We will continue to leverage the patterns we put in place in previous chapters to ensure our ViewModels remain testable and do not have any specific dependencies on the Azure API or even the HTTP communication logic.

  1. First, update the TripLogCoreModule in the core library to register our ITripLogDataService implementation into the IoC:
    public class TripLogCoreModule : NinjectModule
    {
        public override void Load ()
        {
            // ViewModels
            Bind<MainViewModel> ().ToSelf ();
            Bind<DetailViewModel> ().ToSelf ();
            Bind<NewEntryViewModel> ().ToSelf ();
    
            // Core Services
            var tripLogService = new TripLogApiDataService (new Uri("https://<your-service-name>.azurewebsites.net"));
    
            Bind<ITripLogDataService> ()
                .ToMethod (x => tripLogService)
                .InSingletonScope ();
        }
    }
  2. Next, update the MainViewModel constructor to take an ITripLogDataService parameter, which will be provided automatically via dependency injection:
    readonly ITripLogDataService _tripLogService;
    
    public MainViewModel (
        INavService navService,
        ITripLogDataService tripLogService)
        : base (navService)
    {
        _tripLogService = tripLogService;
    
        LogEntries = new ObservableCollection<TripLogEntry> ();
    }
  3. Then, we need to update the LoadEntries method in MainViewModel, replacing the three second delay and hard-coded data population with a call to the live TripLog API via the current ITripLogDataService that was injected into the ViewModel's constructor:
    async Task LoadEntries()
    {
        if (IsBusy)
            return;
    
        IsBusy = true;
    
        try
        {
            var entries = await _tripLogService.GetEntriesAsync ();
    
            LogEntries = new ObservableCollection<TripLogEntry> (entries);
        }
        finally {
            IsBusy = false;
        }
    }

No other changes to MainViewModel are required. Now, when the app is launched, instead of the hard-coded data loading, you will see the items stored in the Azure backend service database.

Now, we need to update the NewEntryViewModel so that when we add a new entry, it is actually saved to the Azure backend through the data service.

  1. Update the NewEntryViewModel constructor to take an ITripLogDataService parameter:
    readonly ITripLogDataService _tripLogService;
    
    public NewEntryViewModel (
        INavService navService,
        ILocationService locService,
        ITripLogDataService tripLogService)
        : base (navService)
    {
        _locService = locService;
        _tripLogService = tripLogService;
    
        Date = DateTime.Today;
        Rating = 1;
    }
  2. Then, we need to update the SaveCommand execute Action method to call the AddEntryAsync method on the data service:
    async Task ExecuteSaveCommand()
    {
        if (IsBusy)
            return;
    
        IsBusy = true;
    
        var newItem = new TripLogEntry {
             Title = this.Title,
             Latitude = this.Latitude,
             Longitude = this.Longitude,
             Date = this.Date,
             Rating = this.Rating,
             Notes = this.Notes
        };
    
        try
        {
            await _tripLogService.AddEntryAsync (newItem);
            await NavService.GoBack ();
        }
        finally {
             IsBusy = false;
        }
    }

Now, if we launch the app, navigate to the new entry page, fill out the form, and click Save, the log entry will be sent to the TripLog backend service and saved in the database.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.239.41