ASP.NET Core Integration
This page shows how to wire ConvergeDB into an ASP.NET Core application using dependency injection.
Registering services
Section titled “Registering services”Register ConvergenceClient as a singleton, then register each KindHandle<T> as a singleton via factory method:
builder.Services.AddSingleton<ConvergenceClient>(_ => ConvergenceClient.ConnectAsync(new ConvergenceOptions { Host = builder.Configuration["Convergence:Host"]!, Port = int.Parse(builder.Configuration["Convergence:Port"]!), SourceId = byte.Parse(builder.Configuration["Convergence:SourceId"]!), }).GetAwaiter().GetResult());
// Register each KindHandle as a singletonbuilder.Services.AddSingleton<KindHandle<Player>>(sp => sp.GetRequiredService<ConvergenceClient>() .RegisterKindAsync<Player>() .GetAwaiter().GetResult());
builder.Services.AddSingleton<KindHandle<OrderView>>(sp => sp.GetRequiredService<ConvergenceClient>() .RegisterKindAsync<OrderView>() .GetAwaiter().GetResult());Configuration
Section titled “Configuration”Add the connection details to appsettings.json:
{ "Convergence": { "Host": "127.0.0.1", "Port": "3727", "SourceId": "1" }}Injecting KindHandle into services
Section titled “Injecting KindHandle into services”Inject KindHandle<T> directly. There is no need to inject ConvergenceClient unless you need low-level access.
public class PlayerService(KindHandle<Player> players){ public Task UpdateAsync(Player p) => players.AssertAsync(p);
public Task<Player?> GetAsync(ReadOnlyMemory<byte> id) => players.QueryAsync(id);}Background subscriber service
Section titled “Background subscriber service”Use a BackgroundService to process subscription notifications:
public class PlayerSubscriberService(KindHandle<Player> players) : BackgroundService{ protected override async Task ExecuteAsync(CancellationToken ct) { await foreach (var change in players.SubscribeAsync( bootstrap: true, ct: ct)) { // Process change... } }}Register it:
builder.Services.AddHostedService<PlayerSubscriberService>();Thread safety
Section titled “Thread safety”ConvergenceClient is thread-safe. Multiple threads can call AssertAsync, QueryAsync, and SubscribeAsync concurrently. A single client instance should be shared across the application rather than creating one per request.
KindHandle<T> is also thread-safe and designed to be used as a singleton.
Complete example
Section titled “Complete example”var builder = WebApplication.CreateBuilder(args);
// ConvergeDB clientbuilder.Services.AddSingleton<ConvergenceClient>(_ => ConvergenceClient.ConnectAsync(new ConvergenceOptions { Host = builder.Configuration["Convergence:Host"]!, Port = int.Parse(builder.Configuration["Convergence:Port"]!), SourceId = byte.Parse(builder.Configuration["Convergence:SourceId"]!), }).GetAwaiter().GetResult());
builder.Services.AddSingleton<KindHandle<Player>>(sp => sp.GetRequiredService<ConvergenceClient>() .RegisterKindAsync<Player>() .GetAwaiter().GetResult());
// Application servicesbuilder.Services.AddScoped<PlayerService>();builder.Services.AddHostedService<PlayerSubscriberService>();
var app = builder.Build();
app.MapGet("/players/{id:guid}", async (Guid id, PlayerService svc) =>{ var player = await svc.GetAsync(EntityId.FromGuid(id)); return player is { } p ? Results.Ok(p) : Results.NotFound();});
app.MapPut("/players/{id:guid}", async (Guid id, Player player, PlayerService svc) =>{ await svc.UpdateAsync(player with { EntityId = EntityId.FromGuid(id) }); return Results.NoContent();});
app.Run();