Skip to content

DotNET client usage

The .NET client resolves a namespace for every event store operation. You can configure namespace resolution by passing an IEventStoreNamespaceResolver implementation to the ChronicleClient constructor.

using Cratis.Chronicle;
public class NamespacesDotNetClientSampleResolver : IEventStoreNamespaceResolver
{
public EventStoreNamespaceName Resolve() => EventStoreNamespaceName.Default;
}

The resolver returns the namespace name to use for the current context.

Always returns the default namespace. Use this when you do not need tenant isolation. This is the default when no resolver is supplied.

Resolves the namespace from the current principal’s claims. This is useful when you have an authenticated user and want to map the tenant from a claim.

using Cratis.Chronicle;
public static class NamespacesDotNetClientClaimsBased
{
public static ChronicleClient CreateWithDefaultClaimType(ChronicleOptions options) =>
// Uses the default claim type "tenant_id"
new(options, namespaceResolver: new ClaimsBasedNamespaceResolver("tenant_id"));
public static ChronicleClient CreateWithCustomClaimType(ChronicleOptions options) =>
// Uses a custom claim type
new(options, namespaceResolver: new ClaimsBasedNamespaceResolver("custom_tenant_claim"));
}

If the claim is not found or the user is not authenticated, the default namespace is used.

For console or direct-client scenarios, pass the resolver as a constructor parameter:

using Cratis.Chronicle;
public static class NamespacesDotNetClientDefaultResolver
{
public static ChronicleClient Create(ChronicleOptions options)
{
var resolver = new DefaultEventStoreNamespaceResolver();
return new ChronicleClient(options, namespaceResolver: resolver);
}
}

When using IHostApplicationBuilder (worker service or web app), configure the resolver through ChronicleClientOptions or directly via IChronicleBuilder:

using Microsoft.Extensions.Hosting;
public static class NamespacesDotNetClientHostedAppConfiguration
{
public static void ConfigureViaOptions(IHostApplicationBuilder builder) =>
// Via options (type is resolved from DI)
builder.AddCratisChronicle(options =>
{
options.EventStore = "my-store";
options.EventStoreNamespaceResolverType = typeof(TenantNamespaceResolver);
});
public static void ConfigureViaBuilder(IHostApplicationBuilder builder, ITenantContext tenantContext) =>
// Via builder (instance is used directly)
builder.AddCratisChronicle(
configureOptions: options => options.EventStore = "my-store",
configure: b => b.WithNamespaceResolver(new TenantNamespaceResolver(tenantContext)));
}

Implement a resolver when your namespace comes from a custom context, such as a tenant provider or a request-scoped service.

If your application uses Arc Tenancy, you can resolve the namespace from its tenant context. See Arc Tenancy.

using Cratis.Chronicle;
public interface ITenantContext
{
EventStoreNamespaceName CurrentTenantId { get; }
}
public class TenantNamespaceResolver : IEventStoreNamespaceResolver
{
readonly ITenantContext _tenantContext;
public TenantNamespaceResolver(ITenantContext tenantContext)
{
_tenantContext = tenantContext;
}
public EventStoreNamespaceName Resolve() =>
_tenantContext.CurrentTenantId;
}
using Cratis.Chronicle;
public static class NamespacesDotNetClientTenantResolverUsage
{
public static ChronicleClient Create(ChronicleOptions options, ITenantContext tenantContext) =>
new(options, namespaceResolver: new TenantNamespaceResolver(tenantContext));
}
  • Keep resolvers deterministic within a request or operation
  • Provide a safe fallback to the default namespace
  • Avoid expensive resolution logic, as it is called frequently

For web applications, see ASP.NET Core namespace resolution.