![]() |
VOOZH | about |
dotnet add package EventStore.Tools.PositionRepository.Gprc --version 1.4.6
NuGet\Install-Package EventStore.Tools.PositionRepository.Gprc -Version 1.4.6
<PackageReference Include="EventStore.Tools.PositionRepository.Gprc" Version="1.4.6" />
<PackageVersion Include="EventStore.Tools.PositionRepository.Gprc" Version="1.4.6" />Directory.Packages.props
<PackageReference Include="EventStore.Tools.PositionRepository.Gprc" />Project file
paket add EventStore.Tools.PositionRepository.Gprc --version 1.4.6
#r "nuget: EventStore.Tools.PositionRepository.Gprc, 1.4.6"
#:package EventStore.Tools.PositionRepository.Gprc@1.4.6
#addin nuget:?package=EventStore.Tools.PositionRepository.Gprc&version=1.4.6Install as a Cake Addin
#tool nuget:?package=EventStore.Tools.PositionRepository.Gprc&version=1.4.6Install as a Cake Tool
A lightweight library for persisting and retrieving the last processed position in a KurrentDB (formerly EventStoreDB) stream. Useful when you need to resume a subscription from where you left off after a restart.
The position is saved to a dedicated KurrentDB stream (capped at 1 event). To avoid excessive writes during fast processing, saves are batched on a configurable interval (default: 1 second).
| Package | Target | Description |
|---|---|---|
EventStore.Tools.PositionRepository.Gprc |
net8.0 | Recommended. Uses KurrentDB.Client (gRPC) |
EventStore.Tools.PositionRepository |
netstandard2.0 | Legacy. Uses the old TCP EventStore.Client |
PM> Install-Package EventStore.Tools.PositionRepository.Gprc
dotnet add package EventStore.Tools.PositionRepository.Gprc
PM> Install-Package EventStore.Tools.PositionRepository
dotnet add package EventStore.Tools.PositionRepository
var client = new KurrentDBClient(KurrentDBClientSettings.Create("esdb://localhost:2113?tls=false"));
var positionRepo = new PositionRepository(
positionStreamName: "my-subscription-position",
positionEventType: "PositionStored",
client: client);
positionRepo.Set(resolvedEvent.OriginalPosition.Value);
if (positionRepo.TryGet(out var position))
{
await client.SubscribeToAllAsync(position, eventAppeared);
}
// Pass interval: 0 to save immediately on every Set() call
var positionRepo = new PositionRepository("my-position", "PositionStored", client, interval: 0);
GrpcSyncService is a ready-made subscription host included in the gRPC package. It manages the KurrentDB catch-up subscription loop, handles reconnections with backoff, and persists the position automatically. You implement only the business logic for the events you care about.
$all stream from the last saved position$-prefixed), Position*, and CloudEvent* event types automaticallydata, metadata both as JsonNode)CatchedUpEvent once the subscription has replayed all historical events and gone liveRegister one ISyncEventHandler per event type and let GrpcSyncService dispatch to them. This is the simplest path and works well with dependency injection.
// 1. Implement a handler for each event type you care about
public class OrderPlacedHandler : ISyncEventHandler
{
public string EventType => "OrderPlacedV1";
public void Handle(JsonNode data, JsonNode metadata)
{
var orderId = data["orderId"]?.GetValue<string>();
// update your read model / projection
}
}
// 2. Register everything in DI (e.g. in Program.cs or a Startup class)
services.AddSingleton<KurrentDBClient>(_ =>
new KurrentDBClient(KurrentDBClientSettings.Create("esdb://localhost:2113?tls=false")));
services.AddSingleton<IPositionRepository>(sp =>
new PositionRepository("orders-projection-position", "PositionStored",
sp.GetRequiredService<KurrentDBClient>()));
services.AddSingleton<ISyncEventHandler, OrderPlacedHandler>();
services.AddSingleton<ISyncEventHandler, OrderCancelledHandler>();
services.AddSingleton<ISyncService>(sp =>
{
var posRepo = sp.GetRequiredService<IPositionRepository>();
var client = sp.GetRequiredService<KurrentDBClient>();
var handlers = sp.GetServices<ISyncEventHandler>();
var logger = sp.GetRequiredService<ILogger<GrpcSyncService>>();
var catchUpFrom = posRepo.TryGet(out var saved)
? FromAll.After(saved)
: FromAll.Start;
return new GrpcSyncService(posRepo, client, catchUpFrom, handlers, logger);
});
// 3. Start/stop via the ISyncService interface
var syncService = sp.GetRequiredService<ISyncService>();
await syncService.StartAsync(cancellationToken);
// React when catch-up is complete (all historical events replayed)
syncService.CatchedUpEvent += (_, _) =>
{
Console.WriteLine("Projection is live.");
return Task.CompletedTask;
};
Subclass GrpcSyncService when you need cross-cutting concerns applied to every event before dispatch — for example tenant filtering, custom logging context, or bridging to a different serialiser.
public class MyProjectionService : GrpcSyncService
{
public MyProjectionService(
IPositionRepository positionRepository,
KurrentDBClient conn,
FromAll catchUpFrom,
IEnumerable<ISyncEventHandler> handlers,
ILogger<MyProjectionService> logger)
: base(positionRepository, conn, catchUpFrom, handlers, logger) { }
// Override to intercept the raw ResolvedEvent before any dispatch
protected override Task EventAppeared(ResolvedEvent evt)
{
// add context, filter, transform — then call base to dispatch normally
using var scope = _logger.BeginScope(new { evt.Event.EventType });
return base.EventAppeared(evt);
}
// Override to intercept after parsing but just before the handler is called
protected override void ProcessEventAppeared(string eventType, JsonNode data, JsonNode metadata)
{
// e.g. enrich data, log, or call a different dispatch mechanism
base.ProcessEventAppeared(eventType, data, metadata);
}
}
GrpcSyncService implements ISyncService which maps directly onto the IHostedService lifecycle. Wire it up as a hosted service:
public class ProjectionWorker : BackgroundService
{
private readonly ISyncService _syncService;
public ProjectionWorker(ISyncService syncService) => _syncService = syncService;
protected override Task ExecuteAsync(CancellationToken stoppingToken)
=> _syncService.StartAsync(stoppingToken);
public override Task StopAsync(CancellationToken cancellationToken)
=> _syncService.StopAsync(cancellationToken);
}
// Register in Program.cs
services.AddHostedService<ProjectionWorker>();
Releases are published to NuGet via GitHub Actions and triggered manually.
NUGET_API_KEY secret must be set in the repository under Settings → Secrets and variables → Actions1.4.4 → 1.4.5), or enter a specific version (e.g. 1.5.0) for a minor/major bumpThe workflow will:
.csprojgprc-<version> tagSame steps as above but use Actions → Publish EventStore.Tools.PositionRepository.
A legacy-<version> tag is created on completion.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on EventStore.Tools.PositionRepository.Gprc:
| Package | Downloads |
|---|---|
|
Linker.Core
Replicate data between KurrentDb (former EventStore) instances |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.4.6 | 274 | 5/22/2026 |
| 1.4.5 | 425 | 5/22/2026 |
| 1.4.4 | 112 | 5/18/2026 |
| 1.4.3 | 2,981 | 6/6/2025 |
| 1.4.2 | 262 | 6/5/2025 |
| 1.4.1 | 257 | 6/5/2025 |
| 1.4.0 | 1,861 | 6/3/2025 |
| 1.3.1 | 294 | 6/3/2025 |
| 1.3.0 | 315 | 5/27/2025 |
| 1.2.0 | 958 | 4/1/2025 |
| 1.1.1 | 209 | 1/27/2025 |
| 1.1.0 | 1,038 | 1/24/2025 |
| 1.0.3 | 883 | 11/7/2024 |
| 1.0.2 | 206 | 11/6/2024 |
| 1.0.1 | 215 | 11/6/2024 |
| 1.0.0 | 654 | 10/16/2023 |
upgrade KurrentDB.Client to 1.4.0 to address security vulnerabilities