• Switch to dark mode
  • Building a Job

    Jobs are easy to build, but understanding how to break down problems is key to making design decisions that will endure the test of time.

    Describe the Problem
    Write Code
    Package and Upload
    Run using the API

    Describe the problem as a list and an action.

    A job can be thought of as a for-each loop, where a block of code executes for each item in a list. This simple construct naturally fits lots of problems. Lists of data can be found in results from API calls, database queries or files on disk.

    API
    { "items": [ { "id": 13, "address": "509 S Carrollton Ave", "isMapped": true }, { "id": 19, "address": "8124 Oak St", "isMapped": false }, { "id": 35, "address": "4801 Magazine St", "isMapped": true } ] }
    Database
    IdAddressIsMapped
    13509 S Carrollton Avetrue
    198124 Oak Stfalse
    354801 Magazine Sttrue
    File
    13,"509 S Carrollton Ave",true
    19,"8124 Oak St",false
    35,"4801 Magazine St",true

    Get a list of items.

    The configuration needed to run the job and the type of item in the list is defined when the base class is extended. Both the config and item must be serializable, which allows the job to play well with the API.

    GetItemsAsync is where the job gets its list of items and returns it for processing.

    using Runly;
    
    public class PlaceMapper : Job<Config, Place>
    {
    	public override IAsyncEnumerable<Place> GetItemsAsync()
    	{
    		return api.GetUnmappedPlaces().ToAsyncEnumerable();
    	}
    }

    Do some work.

    The job does its work in ProcessAsync. Each item is passed to this method, like the body of the for-each loop. Items can be processed in parallel to minimize the total time spent working on the list.

    The Result describes whether the processing was successful or not. Items can be processed again to avoid failures due to temporary issues like network connectivity.

    public override async Task<Result> ProcessAsync(Place place)
    {
    	var coords = await mapper.GetCoordinates(place.Address);
    
    	await api.MapPlace(place.Id, coords);
    
    	return Result.Success();
    }

    Build it in a Console App.

    It all goes into a console app. There are no special project templates or plugins to make this work. The JobHost handles the command line arguments, giving you a CLI right out of the box.

    Project references and dependencies are managed just like any other console app, making the learning curve that much smaller.

    class Program
    {
    	static async Task Main(string[] args)
    	{
    		await JobHost.CreateDefaultBuilder(args)
    			.ConfigureServices((host, services) =>
    			{
    				services.AddHttpClient<IPlaceApi, HttpPlaceApi>();
    			})
    			.Build()
    			.RunJobAsync();
    	}
    }

    Ready to dive in? Create a free account now!