Skip to main content

C# SDK Integration & Implementation Guide

The ResQueServe C# SDK (XENBIT.ResQueServe.SDK) is a strongly-typed client library designed for .NET 9+ applications.

Updated over a month ago

The ResQueServe C# SDK encapsulates the complexity of the ResQueServe REST API, handling authentication, serialization of polymorphic types, and error mapping automatically.

Designed with the Dependency Injection (DI) pattern at its core, this SDK integrates natively with Microsoft.Extensions.Hosting and ASP.NET Core pipelines.


Prerequisites

  • Target Framework: .NET 9.0 or higher

  • Dependencies:

    • Microsoft.Extensions.DependencyInjection

    • Microsoft.Extensions.Configuration

    • RestSharp

    • XENBIT.ResQueServe.Core (Internal dependency, wrapped by SDK)


Installation & Service Registration

The SDK is delivered via the private NuGet feed.

Bash

dotnet add package XENBIT.ResQueServe.SDK.Client

Dependency Injection Setup

The SDK provides a fluent extension method AddResQueServeDashboardClient to register all necessary services (SystemClient, PoiClient, UnitClient, etc.) into your IoC container. The clients are registered with a Scoped lifetime, making them safe to use in web requests or short-lived worker scopes.

Approach A: Configuration via AppSettings (Recommended)

This approach keeps credentials out of your source code and allows for environment-specific transforms.

appsettings.json:

JSON

{
"ResQueServe": {
"BaseUrl": "https://www.sim-dispatcher.com",
"JwtToken": "eyJhbGciOiJIUzI1Ni...",
"TimeoutSeconds": 60
}
}

Program.cs:

C#

using XENBIT.ResQueServe.SDK.DashboardClient.Extensions;

var builder = Host.CreateApplicationBuilder(args);

// Automatically binds to the "ResQueServe" section
builder.Services.AddResQueServeClient(builder.Configuration, sectionName: "ResQueServe");

Approach B: Fluent / Code-Based Configuration

Useful for console applications or scenarios where tokens are retrieved at runtime (e.g., from a KeyVault).

C#

using XENBIT.ResQueServe.SDK.DashboardClient.Extensions;
using XENBIT.ResQueServe.SDK.Shared.Defaults; // Contains default Brand URLs

builder.Services.AddResQueServeClient(options =>
{
// Use pre-defined constants for official environments
options.BaseUrl = BaseUrlDefaults.SIMDispatcherDashboard;

// Set the initial JWT (can be updated later)
options.JwtToken = fetchedToken;

// Adjust timeout for slow operations (e.g., large fleet imports)
options.TimeoutSeconds = 120;
});

The "Info" Data Pattern

To optimize performance and security, the ResQueServe API distinguishes between Full Models (heavy, relational data) and Info Models (lightweight, metadata-rich).

Public Developer Constraint: Public consumers of the SDK are currently restricted to Info endpoints for data retrieval. While the SDK contains methods for fetching full entities internally, your read operations should rely on GetInfoAsync and GetAllAsync.

Operation

Method Pattern

Return Type

Purpose

Search/List

GetAllAsync(...)

Pagination<TInfo>

Fetch lists of objects (e.g., "All Fire Stations").

Inspect

GetInfoAsync(...)

TInfo

Fetch details for a specific ID.

Write

CreateAsync(...)

TInfo

Create a new entity (returns the Info of the created object).

Example: Searching for Data

The GetAllAsync methods support server-side pagination and text search.

C#

public class DataHarvester(PoiClient poiClient)
{
public async Task FindAmbulanceStations(long dispatchCenterId)
{
// Fetch page 1, 20 items per page, searching for "Rescue"
var result = await poiClient.GetAllAsync(
dpcId: dispatchCenterId,
search: "Rescue",
page: 1,
pageSize: 20
);

Console.WriteLine($"Total Matches: {result.TotalCount}");

foreach (var poi in result.Items)
{
// The Info model contains essential data for display/linking
Console.WriteLine($"Found: {poi.Name} (ID: {poi.Id})");

if(poi.Coordinates.IsValid())
{
Console.WriteLine($"Loc: {poi.Coordinates.GetOpenStreetMapUrl()}");
}
}
}
}

Creating and Modifying Resources

When writing data (POST/PUT), you will work with the Interfaces (IUnit, IPoi, IKeyword) from the Abstractions package, or the concrete records from the Core package.

Creating a Unit (Vehicle)

To create a unit, you must instantiate a Unit record. Note that strict validation rules apply (e.g., a Unit must have a Home Station POI).

C#

using XENBIT.ResQueServe.Core.Models; // For concrete 'Unit' record
using XENBIT.ResQueServe.Abstractions.DataStructures; // For Enums

public async Task CreateNewAmbulance(
UnitClient unitClient,
PoiClient poiClient,
long dpcId,
long stationId)
{
// 1. We need the Station reference first
var stationInfo = await poiClient.GetInfoAsync(dpcId, stationId);

// Map Info to a minimal POI object for the relationship
// (The API only needs the ID for the link, but the model requires the object)
var stationRef = new Poi { Id = stationInfo.Id };

// 2. Define the Unit
var newUnit = new Unit
{
Callsign = "Rescue 1-83-1",
CallsignShort = "1-83-1",
Category = UnitCategory.Ambulance, // Enum usage
Poi = stationRef, // Link to Home Station
IsEquippedWithGps = true,
DutyOptions = UnitDutyOptions.RoundTheClock
};

// 3. Send to API
// Returns UnitInfo confirming the ID assigned by the server
var createdInfo = await unitClient.CreateAsync(dpcId, newUnit);

Console.WriteLine($"Unit Created successfully with ID: {createdInfo.Id}");
}

Handling Polymorphic Data

The SDK handles complex polymorphic serialization automatically. For example, IKeyword contains a list of IKeywordEquipment. You can simply add objects to the list, and the SDK's internal JSON converters ensure the API receives the correct @type annotations.


Advanced Client Management

Dynamic Token Updates

For long-running applications (like a background synchronization service), your JWT might expire. The ApiClientBase exposes a method to update the token without re-instantiating the client.

C#

public class BackgroundSync(UnitClient unitClient)
{
public async Task RunSyncJob()
{
// Logic to check token expiration...
string newToken = await FetchNewTokenFromAuthServer();

// Updates the token for all future requests made by this instance
unitClient.UpdateToken(newToken);

await unitClient.GetAllAsync(...);
}
}

System Health Checks

Before starting complex operations, it is best practice to verify connectivity using the SystemClient.

C#

public async Task<bool> IsServerHealthy(SystemClient sysClient)
{
try
{
var info = await sysClient.GetSystemInfoAsync();
Console.WriteLine($"Connected to API v{info.VersionApi}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
return false;
}
}

Best Practices

  1. Scoped Lifetime: Do not capture the clients in a Singleton service. They are designed as Scoped. If you need them in a background IHostedService (Singleton), create a scope:

    C#

    using (var scope = _serviceProvider.CreateScope())
    {
    var unitClient = scope.ServiceProvider.GetRequiredService<UnitClient>();
    // Work with client...
    }
  2. Exception Handling: The SDK throws specific exceptions (ValidationException, DataNotFoundException). Always wrap your write operations in try/catch blocks (see this Article for details).

  3. Trace IDs: If you encounter a 500 error, inspect the ResQueServeException.Problems or the internal logger output for the TraceId. This is required for support.

Did this answer your question?