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.DependencyInjectionMicrosoft.Extensions.ConfigurationRestSharpXENBIT.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 |
|
| Fetch lists of objects (e.g., "All Fire Stations"). |
Inspect |
|
| Fetch details for a specific ID. |
Write |
|
| 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
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...
}Exception Handling: The SDK throws specific exceptions (
ValidationException,DataNotFoundException). Always wrap your write operations intry/catchblocks (see this Article for details).Trace IDs: If you encounter a 500 error, inspect the
ResQueServeException.Problemsor the internal logger output for theTraceId. This is required for support.
