Build a Service that Fetches Dynamic Data

When extending Geocortex Mobile, you may find yourself creating multiple components that have a shared concern, like a data source, or a REST endpoint call. Shared concerns like this present a good use case for creating a custom service. Implementing a custom service in Geocortex Mobile allows you to implement logic and shared resources that are available on startup to any component. Custom Services are also the recommended way of registering implementations for custom commands and operations.

This article will guide you through creating a custom service that fetches data from a dynamic source and delivers it to components through an operation.

Prerequisites

Check out and setup the Geocortex Mobile SDK Quickstart project.

Create a New Service

Create a new file services/CustomService.cs under the platform agnostic project. In the file, add a new component class CustomService and register it with Autofac.

App1/App1/services/CustomService.cs
using App1.Services;
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Composition.Services;
using System.Threading.Tasks;
[assembly: Service(typeof(CustomService), PropertiesAutowired = true)]
namespace App1.Services
{
class CustomService : ServiceBase
{
public CustomService()
:base()
{
// on creation logic here
}
protected override Task DoInitialize()
{
// on initialization logic here
return Task.CompletedTask;
}
}
}

Next, we can mock a dynamically updating data source that the service consumes. In a real application, this might be a webhook, or an RSS feed, or database.

note

It's essential to properly dispose of any managed resources, like the background thread in this example, to prevent memory leaks.

App1/App1/services/CustomService.cs
using App1.Services;
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Composition.Logging;
using VertiGIS.Mobile.Composition.Services;
using System.Threading;
using System.Threading.Tasks;
[assembly: Service(typeof(CustomService), PropertiesAutowired = true)]
namespace App1.Services
{
class CustomService : ServiceBase
{
private bool disposed = false;
private Thread dataUpdater;
private int data = 0;
private async void _dataUpdater()
{
try
{
while (true)
{
// Fetch data from a rest endpoint
await Task.Run(() =>
{
// await data.fetch()
data = data + 1;
Logger.Debug($"Custom Service fetched new data. Current Value: {data}");
});
Thread.Sleep(3000);
}
}
catch (ThreadAbortException)
{
return;
}
}
public CustomService()
: base()
{
dataUpdater = new Thread(new ThreadStart(_dataUpdater));
}
protected override Task DoInitialize()
{
dataUpdater.Start();
return Task.CompletedTask;
}
protected override void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
dataUpdater.Abort();
}
disposed = true;
base.Dispose(disposing);
}
}
}

To expose this data source to other components and services, we can create a new operation, custom-data.fetch.

First, create a new operations class and define a new operation.

App1/App1/services/CustomOperations.cs
using App1.Operations;
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Composition.Messaging;
using VertiGIS.Mobile.Infrastructure.Messaging;
[assembly: Export(typeof(CustomOperations), SingleInstance = true)]
namespace App1.Operations
{
class CustomOperations : OperationsBase
{
public IOperation<int> FetchCustomData=> GetOperation<int>("custom-data.fetch");
public CustomOperations(IOperationRegistry operationRegistry)
: base(operationRegistry)
{
}
}
}

Next, inject the operation into your custom service and register an implementation that returns the dynamic data.

App1/App1/services/CustomService.cs
using App1.Operations;
using App1.Services;
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Composition.Logging;
using VertiGIS.Mobile.Composition.Messaging;
using VertiGIS.Mobile.Composition.Services;
using System.Threading;
using System.Threading.Tasks;
[assembly: Service(typeof(CustomService), PropertiesAutowired = true)]
namespace App1.Services
{
class CustomService : ServiceBase
{
private Thread dataUpdater;
private int data = 0;
private async void _dataUpdater()
{
try
{
while (true)
{
// Fetch data from a rest endpoint
await Task.Run(() =>
{
// await data.fetch()
data = data + 1;
Logger.Debug($"Custom Service fetched new data. Current Value: {data}");
});
Thread.Sleep(3000);
}
}
catch (ThreadAbortException)
{
return;
}
}
private async Task<int> FetchData()
{
return data;
}
public CustomService(CustomOperations customOperations)
:base()
{
// on creation logic here
customOperations.FetchCustomData.RegisterExecute(FetchData, this);
dataUpdater = new Thread(new ThreadStart(_dataUpdater));
}
protected override Task DoInitialize()
{
// on initialization logic here
dataUpdater.Start();
return Task.CompletedTask;
}
protected override void Dispose(bool disposing)
{
// dispose of any held resources here
dataUpdater.Abort();
base.Dispose(disposing);
}
}
}

You've now built a custom service that fetches from a dynamic data source and exposes that data to other components! You could take this farther by creating an event that informs components and services when the data has changed, or returning a reference from the fetch data operation that can be observed for changes.

Relevant SDK Samples

Check out the relevant Geocortex Mobile SDK Samples:

Next Steps

Learn More About Services

Take a deep dive into services in the Geocortex Mobile SDK