diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 75d79ccb0..6dd544056 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -30,6 +30,19 @@ jobs:
uses: actions/checkout@v5
with:
fetch-depth: 0
+ -
+ name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 10.0
+ -
+ name: Login to Docker Hub
+ if: ${{ github.event.pull_request.head.repo.fork == false }}
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKER_USER }}
+ password: ${{ secrets.DOCKER_TOKEN }}
-
name: Run tests
run: dotnet test --framework net10.0
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c48a65ecc..dfcedb826 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,8 +3,8 @@
true
- 10.0.0-rc.2.25502.107
- 10.0.0-rc.2.25502.107
+ 10.0.0
+ 10.0.0
9.0.10
@@ -38,11 +38,9 @@
+
-
-
-
diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props
index a21a2e27a..d99b79ebf 100644
--- a/samples/Directory.Build.props
+++ b/samples/Directory.Build.props
@@ -1,6 +1,6 @@
- net9.0
+ net10.0
enable
enable
preview
diff --git a/samples/esdb/Bookings.Domain/Bookings/BookingState.cs b/samples/esdb/Bookings.Domain/Bookings/BookingState.cs
index 15fbda349..886803764 100644
--- a/samples/esdb/Bookings.Domain/Bookings/BookingState.cs
+++ b/samples/esdb/Bookings.Domain/Bookings/BookingState.cs
@@ -28,17 +28,17 @@ public BookingState() {
static BookingState HandlePayment(BookingState state, V1.PaymentRecorded e)
=> state with {
- Outstanding = new Money { Amount = e.Outstanding, Currency = e.Currency },
- Payments = state.Payments.Add(new PaymentRecord(e.PaymentId, new Money(e.PaidAmount, e.Currency)))
+ Outstanding = new() { Amount = e.Outstanding, Currency = e.Currency },
+ Payments = state.Payments.Add(new(e.PaymentId, new(e.PaidAmount, e.Currency)))
};
static BookingState HandleBooked(BookingState state, V1.RoomBooked booked)
=> state with {
- RoomId = new RoomId(booked.RoomId),
- Period = new StayPeriod(booked.CheckInDate, booked.CheckOutDate),
+ RoomId = new(booked.RoomId),
+ Period = new(booked.CheckInDate, booked.CheckOutDate),
GuestId = booked.GuestId,
- Price = new Money { Amount = booked.BookingPrice, Currency = booked.Currency },
- Outstanding = new Money { Amount = booked.OutstandingAmount, Currency = booked.Currency }
+ Price = new() { Amount = booked.BookingPrice, Currency = booked.Currency },
+ Outstanding = new() { Amount = booked.OutstandingAmount, Currency = booked.Currency }
};
}
diff --git a/samples/esdb/Bookings.Payments/Application/CommandService.cs b/samples/esdb/Bookings.Payments/Application/CommandService.cs
index 12e49a6e6..7f913659d 100644
--- a/samples/esdb/Bookings.Payments/Application/CommandService.cs
+++ b/samples/esdb/Bookings.Payments/Application/CommandService.cs
@@ -10,7 +10,7 @@ public CommandService(IEventStore store) : base(store) {
On()
.InState(ExpectedState.New)
.GetId(cmd => new(cmd.PaymentId))
- .Act((payment, cmd) => payment.ProcessPayment(cmd.BookingId, new Money(cmd.Amount, cmd.Currency), cmd.Method, cmd.Provider));
+ .Act((payment, cmd) => payment.ProcessPayment(cmd.BookingId, new(cmd.Amount, cmd.Currency), cmd.Method, cmd.Provider));
}
}
diff --git a/samples/esdb/Bookings.Payments/Bookings.Payments.csproj b/samples/esdb/Bookings.Payments/Bookings.Payments.csproj
index a604b03a8..42d100cf3 100644
--- a/samples/esdb/Bookings.Payments/Bookings.Payments.csproj
+++ b/samples/esdb/Bookings.Payments/Bookings.Payments.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/samples/esdb/Bookings.Payments/Integration/Payments.cs b/samples/esdb/Bookings.Payments/Integration/Payments.cs
index 737f05738..8b724e326 100644
--- a/samples/esdb/Bookings.Payments/Integration/Payments.cs
+++ b/samples/esdb/Bookings.Payments/Integration/Payments.cs
@@ -11,16 +11,16 @@ namespace Bookings.Payments.Integration;
public static class PaymentsGateway {
static readonly StreamName Stream = new("PaymentsIntegration");
- public static ValueTask[]> Transform(IMessageConsumeContext original) {
+ public static ValueTask[]> Transform(IMessageConsumeContext original) {
var result = original.Message is PaymentEvents.PaymentRecorded evt
- ? new GatewayMessage(
+ ? new GatewayMessage(
Stream,
new BookingPaymentRecorded(original.Stream.GetId(), evt.BookingId, evt.Amount, evt.Currency),
new(),
new()
)
: null;
- GatewayMessage[] gatewayMessages = result != null ? [result] : [];
+ GatewayMessage[] gatewayMessages = result != null ? [result] : [];
return ValueTask.FromResult(gatewayMessages);
}
}
diff --git a/samples/esdb/Bookings.Payments/Registrations.cs b/samples/esdb/Bookings.Payments/Registrations.cs
index 5110a8ab6..ce4d67664 100644
--- a/samples/esdb/Bookings.Payments/Registrations.cs
+++ b/samples/esdb/Bookings.Payments/Registrations.cs
@@ -14,40 +14,42 @@
namespace Bookings.Payments;
public static class Registrations {
- public static void AddServices(this IServiceCollection services, IConfiguration configuration) {
- services.AddKurrentDBClient(configuration["EventStore:ConnectionString"]!);
- services.AddEventStore();
- services.AddCommandService();
- services.AddSingleton(Mongo.ConfigureMongo(configuration));
- services.AddCheckpointStore();
- services.AddProducer();
+ extension(IServiceCollection services) {
+ public void AddServices(IConfiguration configuration) {
+ services.AddKurrentDBClient(configuration["EventStore:ConnectionString"]!);
+ services.AddEventStore();
+ services.AddCommandService();
+ services.AddSingleton(Mongo.ConfigureMongo(configuration));
+ services.AddCheckpointStore();
+ services.AddProducer();
- services
- .AddGateway(
- "IntegrationSubscription",
- PaymentsGateway.Transform
- );
- }
+ services
+ .AddGateway(
+ "IntegrationSubscription",
+ PaymentsGateway.Transform
+ );
+ }
- public static void AddTelemetry(this IServiceCollection services) {
- services.AddOpenTelemetry()
- .WithMetrics(
- builder => builder
- .AddAspNetCoreInstrumentation()
- .AddEventuous()
- .AddEventuousSubscriptions()
- .AddPrometheusExporter()
- );
+ public void AddTelemetry() {
+ services.AddOpenTelemetry()
+ .WithMetrics(
+ builder => builder
+ .AddAspNetCoreInstrumentation()
+ .AddEventuous()
+ .AddEventuousSubscriptions()
+ .AddPrometheusExporter()
+ );
- services.AddOpenTelemetry()
- .WithTracing(
- builder => builder
- .AddAspNetCoreInstrumentation()
- .AddGrpcClientInstrumentation()
- .AddEventuousTracing()
- .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("payments"))
- .SetSampler(new AlwaysOnSampler())
- .AddZipkinExporter()
- );
+ services.AddOpenTelemetry()
+ .WithTracing(
+ builder => builder
+ .AddAspNetCoreInstrumentation()
+ .AddGrpcClientInstrumentation()
+ .AddEventuousTracing()
+ .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("payments"))
+ .SetSampler(new AlwaysOnSampler())
+ .AddZipkinExporter()
+ );
+ }
}
}
diff --git a/samples/esdb/Bookings/Application/Queries/MyBookingsProjection.cs b/samples/esdb/Bookings/Application/Queries/MyBookingsProjection.cs
index ee7c14cfe..9301e77f8 100644
--- a/samples/esdb/Bookings/Application/Queries/MyBookingsProjection.cs
+++ b/samples/esdb/Bookings/Application/Queries/MyBookingsProjection.cs
@@ -12,7 +12,7 @@ public MyBookingsProjection(IMongoDatabase database) : base(database) {
.UpdateFromContext((ctx, update) =>
update.AddToSet(
x => x.Bookings,
- new MyBookings.Booking(ctx.Stream.GetId(),
+ new(ctx.Stream.GetId(),
ctx.Message.CheckInDate,
ctx.Message.CheckOutDate,
ctx.Message.BookingPrice
diff --git a/samples/esdb/Bookings/Bookings.csproj b/samples/esdb/Bookings/Bookings.csproj
index 7403c67c2..93ba8b391 100644
--- a/samples/esdb/Bookings/Bookings.csproj
+++ b/samples/esdb/Bookings/Bookings.csproj
@@ -25,7 +25,7 @@
-
+
diff --git a/samples/esdb/Bookings/Infrastructure/Mongo.cs b/samples/esdb/Bookings/Infrastructure/Mongo.cs
index e54e029a0..74a5e8020 100644
--- a/samples/esdb/Bookings/Infrastructure/Mongo.cs
+++ b/samples/esdb/Bookings/Infrastructure/Mongo.cs
@@ -11,7 +11,7 @@ public static IMongoDatabase ConfigureMongo(IConfiguration configuration) {
var settings = MongoClientSettings.FromConnectionString(config!.ConnectionString);
if (config is { User: not null, Password: not null }) {
- settings.Credential = new MongoCredential(
+ settings.Credential = new(
null,
new MongoInternalIdentity("admin", config.User),
new PasswordEvidence(config.Password)
diff --git a/samples/esdb/Bookings/Registrations.cs b/samples/esdb/Bookings/Registrations.cs
index 46d6cf369..5bc7d77c8 100644
--- a/samples/esdb/Bookings/Registrations.cs
+++ b/samples/esdb/Bookings/Registrations.cs
@@ -32,7 +32,7 @@ public void AddEventuous(IConfiguration configuration) {
services.AddCommandService();
services.AddSingleton((_, _) => new(true));
- services.AddSingleton((from, currency) => new Money(from.Amount * 2, currency));
+ services.AddSingleton((from, currency) => new(from.Amount * 2, currency));
services.AddSingleton(Mongo.ConfigureMongo(configuration));
diff --git a/samples/postgres/Bookings.Payments/Integration/Payments.cs b/samples/postgres/Bookings.Payments/Integration/Payments.cs
index 6d9d703ad..4ecaa041b 100644
--- a/samples/postgres/Bookings.Payments/Integration/Payments.cs
+++ b/samples/postgres/Bookings.Payments/Integration/Payments.cs
@@ -16,7 +16,7 @@ public static ValueTask[]> Transform(IMes
? new GatewayMessage(
Stream,
new BookingPaymentRecorded(original.Stream.GetId(), evt.BookingId, evt.Amount, evt.Currency),
- new Metadata(),
+ new(),
ProduceOptions
)
: null;
diff --git a/samples/postgres/Bookings.Payments/Registrations.cs b/samples/postgres/Bookings.Payments/Registrations.cs
index 3e420228e..78a4d48f6 100644
--- a/samples/postgres/Bookings.Payments/Registrations.cs
+++ b/samples/postgres/Bookings.Payments/Registrations.cs
@@ -13,7 +13,7 @@ namespace Bookings.Payments;
public static class Registrations {
public static void AddEventuous(this IServiceCollection services, IConfiguration configuration) {
var connectionFactory = new ConnectionFactory {
- Uri = new Uri(configuration["RabbitMq:ConnectionString"]!),
+ Uri = new(configuration["RabbitMq:ConnectionString"]!),
DispatchConsumersAsync = true
};
services.AddSingleton(connectionFactory);
diff --git a/samples/postgres/Bookings/Application/Queries/MyBookingsProjection.cs b/samples/postgres/Bookings/Application/Queries/MyBookingsProjection.cs
index ee7c14cfe..9301e77f8 100644
--- a/samples/postgres/Bookings/Application/Queries/MyBookingsProjection.cs
+++ b/samples/postgres/Bookings/Application/Queries/MyBookingsProjection.cs
@@ -12,7 +12,7 @@ public MyBookingsProjection(IMongoDatabase database) : base(database) {
.UpdateFromContext((ctx, update) =>
update.AddToSet(
x => x.Bookings,
- new MyBookings.Booking(ctx.Stream.GetId(),
+ new(ctx.Stream.GetId(),
ctx.Message.CheckInDate,
ctx.Message.CheckOutDate,
ctx.Message.BookingPrice
diff --git a/samples/postgres/Bookings/Registrations.cs b/samples/postgres/Bookings/Registrations.cs
index b13933f27..78a7040a9 100644
--- a/samples/postgres/Bookings/Registrations.cs
+++ b/samples/postgres/Bookings/Registrations.cs
@@ -37,7 +37,7 @@ public static void AddEventuous(this IServiceCollection services, IConfiguration
services.AddSingleton((_, _) => new(true));
services.AddSingleton(
- (from, currency) => new Money(from.Amount * 2, currency)
+ (from, currency) => new(from.Amount * 2, currency)
);
services.AddSingleton(Mongo.ConfigureMongo(configuration));
diff --git a/src/Core/gen/Eventuous.Shared.Generators/TypeMappingsGenerator.cs b/src/Core/gen/Eventuous.Shared.Generators/TypeMappingsGenerator.cs
index be51e8053..a428e14c3 100644
--- a/src/Core/gen/Eventuous.Shared.Generators/TypeMappingsGenerator.cs
+++ b/src/Core/gen/Eventuous.Shared.Generators/TypeMappingsGenerator.cs
@@ -172,12 +172,11 @@ static void Generate(SourceProductionContext context, string assemblyName, Immut
foreach (var m in distinct) {
// Prefer passing the explicit name when we have it, so runtime reflection is avoided
- if (!string.IsNullOrEmpty(m.EventTypeName)) {
- sb.AppendLine($" mapper.AddType(typeof({m.FullyQualifiedType}), \"{EscapeString(m.EventTypeName)}\");");
- }
- else {
- sb.AppendLine($" mapper.AddType(typeof({m.FullyQualifiedType}));");
- }
+ sb.AppendLine(
+ !string.IsNullOrEmpty(m.EventTypeName)
+ ? $" mapper.AddType(typeof({m.FullyQualifiedType}), \"{EscapeString(m.EventTypeName)}\");"
+ : $" mapper.AddType(typeof({m.FullyQualifiedType}));"
+ );
}
sb.AppendLine(" }");
diff --git a/src/Core/gen/Eventuous.Subscriptions.Generators/ConsumeContextConverterGenerator.cs b/src/Core/gen/Eventuous.Subscriptions.Generators/ConsumeContextConverterGenerator.cs
index 822c6cda4..194d9a0f8 100644
--- a/src/Core/gen/Eventuous.Subscriptions.Generators/ConsumeContextConverterGenerator.cs
+++ b/src/Core/gen/Eventuous.Subscriptions.Generators/ConsumeContextConverterGenerator.cs
@@ -25,7 +25,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) {
static bool IsPotentialUsage(SyntaxNode node, CancellationToken _) {
return node switch {
- GenericNameSyntax { TypeArgumentList.Arguments.Count: 1 } g => g.Identifier.Text == "IMessageConsumeContext" || g.Identifier.Text == "On",
+ GenericNameSyntax { TypeArgumentList.Arguments.Count: 1 } g => g.Identifier.Text is "IMessageConsumeContext" or "On",
// handle qualified names like Eventuous.Subscriptions.Context.IMessageConsumeContext
QualifiedNameSyntax { Right: GenericNameSyntax { TypeArgumentList.Arguments.Count: 1 } g2 } => g2.Identifier.Text == "IMessageConsumeContext",
// implicit: lambdas where parameter type is inferred to IMessageConsumeContext
diff --git a/src/Core/src/Eventuous.Application/AggregateService/CommandHandlerBuilder.cs b/src/Core/src/Eventuous.Application/AggregateService/CommandHandlerBuilder.cs
index 2deda727b..264307644 100644
--- a/src/Core/src/Eventuous.Application/AggregateService/CommandHandlerBuilder.cs
+++ b/src/Core/src/Eventuous.Application/AggregateService/CommandHandlerBuilder.cs
@@ -205,7 +205,7 @@ IDefineAppendAmendment IDefineExecution.
}
IDefineExecution IDefineStore.ResolveStore(Func resolveStore) {
- Ensure.NotNull(resolveStore, nameof(resolveStore));
+ Ensure.NotNull(resolveStore);
_reader = resolveStore;
_writer = resolveStore;
diff --git a/src/Core/src/Eventuous.Application/FunctionalService/CommandHandlerBuilder.cs b/src/Core/src/Eventuous.Application/FunctionalService/CommandHandlerBuilder.cs
index 0cdf2983f..6e9dada8f 100644
--- a/src/Core/src/Eventuous.Application/FunctionalService/CommandHandlerBuilder.cs
+++ b/src/Core/src/Eventuous.Application/FunctionalService/CommandHandlerBuilder.cs
@@ -232,13 +232,13 @@ RegisteredHandler Build() {
);
Func DefaultResolveWriter() {
- ArgumentNullException.ThrowIfNull(writer, nameof(writer));
+ ArgumentNullException.ThrowIfNull(writer);
return _ => writer;
}
Func DefaultResolveReader() {
- ArgumentNullException.ThrowIfNull(reader, nameof(reader));
+ ArgumentNullException.ThrowIfNull(reader);
return _ => reader;
}
diff --git a/src/Core/src/Eventuous.Diagnostics/ActivityExtensions.cs b/src/Core/src/Eventuous.Diagnostics/ActivityExtensions.cs
index 25cac0cfa..76d2cc704 100644
--- a/src/Core/src/Eventuous.Diagnostics/ActivityExtensions.cs
+++ b/src/Core/src/Eventuous.Diagnostics/ActivityExtensions.cs
@@ -4,60 +4,62 @@
namespace Eventuous.Diagnostics;
public static class ActivityExtensions {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static string? GetParentTag(this Activity activity, string tag)
- => activity.Parent?.Tags.FirstOrDefault(x => x.Key == tag).Value;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Activity CopyParentTag(this Activity activity, string tag, string? parentTag = null) {
- var value = activity.GetParentTag(parentTag ?? tag);
- if (value != null) activity.SetTag(tag, value);
- return activity;
- }
+ extension(Activity activity) {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ string? GetParentTag(string tag)
+ => activity.Parent?.Tags.FirstOrDefault(x => x.Key == tag).Value;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Activity SetOrCopyParentTag(this Activity activity, string tag, string? value, string? parentTag = null) {
- var val = value ?? activity.GetParentTag(parentTag ?? tag);
- if (val != null) activity.SetTag(tag, val);
- return activity;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Activity CopyParentTag(string tag, string? parentTag = null) {
+ var value = activity.GetParentTag(parentTag ?? tag);
+ if (value != null) activity.SetTag(tag, value);
+ return activity;
+ }
- public static TracingMeta GetTracingData(this Activity activity)
- => new(activity.TraceId.ToString(), activity.SpanId.ToString());
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Activity SetOrCopyParentTag(string tag, string? value, string? parentTag = null) {
+ var val = value ?? activity.GetParentTag(parentTag ?? tag);
+ if (val != null) activity.SetTag(tag, val);
+ return activity;
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Activity SetActivityStatus(this Activity activity, ActivityStatus status) {
- var (activityStatusCode, description, exception) = status;
+ public TracingMeta GetTracingData()
+ => new(activity.TraceId.ToString(), activity.SpanId.ToString());
- var statusCode = activityStatusCode switch {
- ActivityStatusCode.Error => "ERROR",
- ActivityStatusCode.Ok => "OK",
- _ => "UNSET"
- };
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Activity SetActivityStatus(ActivityStatus status) {
+ var (activityStatusCode, description, exception) = status;
- activity.SetStatus(activityStatusCode, description);
- activity.SetTag(TelemetryTags.Otel.StatusCode, statusCode);
- activity.SetTag(TelemetryTags.Otel.StatusDescription, description);
+ var statusCode = activityStatusCode switch {
+ ActivityStatusCode.Error => "ERROR",
+ ActivityStatusCode.Ok => "OK",
+ _ => "UNSET"
+ };
- return !activity.IsAllDataRequested ? activity : activity.SetException(exception);
- }
+ activity.SetStatus(activityStatusCode, description);
+ activity.SetTag(TelemetryTags.Otel.StatusCode, statusCode);
+ activity.SetTag(TelemetryTags.Otel.StatusDescription, description);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static Activity SetException(this Activity activity, Exception? exception) {
- if (exception == null) return activity;
+ return !activity.IsAllDataRequested ? activity : activity.SetException(exception);
+ }
- var tags = new ActivityTagsCollection(
- [
- new(TelemetryTags.Exception.Type, exception.GetType().Name),
- new(TelemetryTags.Exception.Message, $"{exception.Message} {exception.InnerException?.Message}"),
- new(TelemetryTags.Exception.Stacktrace, exception.StackTrace)
- ]
- );
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ Activity SetException(Exception? exception) {
+ if (exception == null) return activity;
- foreach (var (key, value) in tags) {
- activity.SetTag(key, value);
- }
+ var tags = new ActivityTagsCollection(
+ [
+ new(TelemetryTags.Exception.Type, exception.GetType().Name),
+ new(TelemetryTags.Exception.Message, $"{exception.Message} {exception.InnerException?.Message}"),
+ new(TelemetryTags.Exception.Stacktrace, exception.StackTrace)
+ ]
+ );
- return activity.AddEvent(new(TelemetryTags.Exception.EventName, DateTimeOffset.Now, tags));
+ foreach (var (key, value) in tags) {
+ activity.SetTag(key, value);
+ }
+
+ return activity.AddEvent(new(TelemetryTags.Exception.EventName, DateTimeOffset.Now, tags));
+ }
}
}
diff --git a/src/Core/src/Eventuous.Diagnostics/MetadataExtensions.cs b/src/Core/src/Eventuous.Diagnostics/MetadataExtensions.cs
index 9fa6c4786..ed473967e 100644
--- a/src/Core/src/Eventuous.Diagnostics/MetadataExtensions.cs
+++ b/src/Core/src/Eventuous.Diagnostics/MetadataExtensions.cs
@@ -6,29 +6,31 @@ namespace Eventuous.Diagnostics;
using static DiagnosticTags;
public static class MetadataExtensions {
- public static Metadata AddActivityTags(this Metadata metadata, Activity? activity) {
- if (activity == null) return metadata;
+ extension(Metadata metadata) {
+ public Metadata AddActivityTags(Activity? activity) {
+ if (activity == null) return metadata;
- var tags = activity.Tags.Where(x => x.Value != null && MetaMappings.TelemetryToInternalTagsMap.ContainsKey(x.Key));
+ var tags = activity.Tags.Where(x => x.Value != null && MetaMappings.TelemetryToInternalTagsMap.ContainsKey(x.Key));
- foreach (var (key, value) in tags) {
- metadata.With(MetaMappings.TelemetryToInternalTagsMap[key], value!);
+ foreach (var (key, value) in tags) {
+ metadata.With(MetaMappings.TelemetryToInternalTagsMap[key], value!);
+ }
+
+ return metadata.AddTracingMeta(activity.GetTracingData());
}
- return metadata.AddTracingMeta(activity.GetTracingData());
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ Metadata AddTracingMeta(TracingMeta tracingMeta)
+ => metadata.ContainsKey(TraceId) || tracingMeta.TraceId == EmptyId
+ ? metadata // don't override existing tracing data
+ : metadata
+ .AddNotNull(TraceId, tracingMeta.TraceId)
+ .AddNotNull(SpanId, tracingMeta.SpanId);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TracingMeta GetTracingMeta()
+ => new(metadata.GetString(TraceId), metadata.GetString(SpanId));
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static Metadata AddTracingMeta(this Metadata metadata, TracingMeta tracingMeta)
- => metadata.ContainsKey(TraceId) || tracingMeta.TraceId == EmptyId
- ? metadata // don't override existing tracing data
- : metadata
- .AddNotNull(TraceId, tracingMeta.TraceId)
- .AddNotNull(SpanId, tracingMeta.SpanId);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static TracingMeta GetTracingMeta(this Metadata metadata)
- => new(metadata.GetString(TraceId), metadata.GetString(SpanId));
-
const string EmptyId = "0000000000000000";
}
diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs
index 0e844ce58..c8e3143ff 100644
--- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs
+++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs
@@ -7,172 +7,168 @@ namespace Eventuous;
using static Diagnostics.PersistenceEventSource;
public static class AggregatePersistenceExtensions {
- ///
- /// Store aggregate changes to the event store
- ///
/// Event writer or event store
- /// Stream name for the aggregate
- /// Aggregate instance
- /// Optional: function to add extra information to the event before it gets stored
- /// Cancellation token
- /// Aggregate type
- /// Aggregate state type
- /// Append event result
- /// Gets thrown if the expected stream version mismatches with the given original stream version
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task StoreAggregate(
- this IEventWriter eventWriter,
- StreamName streamName,
- TAggregate aggregate,
- AmendEvent? amendEvent = null,
- CancellationToken cancellationToken = default
- ) where TAggregate : Aggregate where TState : State, new() {
- Ensure.NotNull(aggregate);
-
- try {
- return await eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken).NoContext();
- } catch (OptimisticConcurrencyException e) {
- Log.UnableToStoreAggregate(streamName, e);
-
- throw e.InnerException is null ? new OptimisticConcurrencyException(streamName, e) : new(streamName, e.InnerException);
+ extension(IEventWriter eventWriter) {
+ ///
+ /// Store aggregate changes to the event store
+ ///
+ /// Stream name for the aggregate
+ /// Aggregate instance
+ /// Optional: function to add extra information to the event before it gets stored
+ /// Cancellation token
+ /// Aggregate type
+ /// Aggregate state type
+ /// Append event result
+ /// Gets thrown if the expected stream version mismatches with the given original stream version
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task StoreAggregate(
+ StreamName streamName,
+ TAggregate aggregate,
+ AmendEvent? amendEvent = null,
+ CancellationToken cancellationToken = default
+ ) where TAggregate : Aggregate where TState : State, new() {
+ Ensure.NotNull(aggregate);
+
+ try {
+ return await eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken).NoContext();
+ } catch (OptimisticConcurrencyException e) {
+ Log.UnableToStoreAggregate(streamName, e);
+
+ throw e.InnerException is null ? new OptimisticConcurrencyException(streamName, e) : new(streamName, e.InnerException);
+ }
}
- }
- ///
- /// Store aggregate changes to the event store
- ///
- /// Event writer or event store
- /// Aggregate instance
- /// Aggregate identity
- /// Optional: stream name map
- /// Optional: function to add extra information to the event before it gets stored
- /// Cancellation token
- /// Aggregate type
- /// Aggregate state type
- /// Aggregate identity type
- /// Append event result
- /// Gets thrown if the expected stream version mismatches with the given original stream version
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static Task StoreAggregate(
- this IEventWriter eventWriter,
- TAggregate aggregate,
- TId id,
- StreamNameMap? streamNameMap = null,
- AmendEvent? amendEvent = null,
- CancellationToken cancellationToken = default
- ) where TAggregate : Aggregate where TState : State, new() where TId : Id {
- Ensure.NotNull(aggregate);
-
- if (aggregate.State is State stateWithId && stateWithId.Id != id) {
- throw new InvalidOperationException($"Provided aggregate id {id} doesn't match an existing aggregate id {stateWithId.Id}");
+ ///
+ /// Store aggregate changes to the event store
+ ///
+ /// Aggregate instance
+ /// Aggregate identity
+ /// Optional: stream name map
+ /// Optional: function to add extra information to the event before it gets stored
+ /// Cancellation token
+ /// Aggregate type
+ /// Aggregate state type
+ /// Aggregate identity type
+ /// Append event result
+ /// Gets thrown if the expected stream version mismatches with the given original stream version
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public Task StoreAggregate(
+ TAggregate aggregate,
+ TId id,
+ StreamNameMap? streamNameMap = null,
+ AmendEvent? amendEvent = null,
+ CancellationToken cancellationToken = default
+ ) where TAggregate : Aggregate where TState : State, new() where TId : Id {
+ Ensure.NotNull(aggregate);
+
+ if (aggregate.State is State stateWithId && stateWithId.Id != id) {
+ throw new InvalidOperationException($"Provided aggregate id {id} doesn't match an existing aggregate id {stateWithId.Id}");
+ }
+
+ var streamName = streamNameMap?.GetStreamName(id) ?? StreamNameFactory.For(id);
+
+ return eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken);
}
- var streamName = streamNameMap?.GetStreamName(id) ?? StreamNameFactory.For(id);
-
- return eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken);
- }
-
- ///
- /// Store aggregate changes to the event store
- ///
- /// Event writer or event store
- /// Aggregate instance
- /// Optional: stream name map
- /// Optional: function to add extra information to the event before it gets stored
- /// Cancellation token
- /// Aggregate type
- /// Aggregate state type
- /// Aggregate identity type
- /// Append event result
- /// Gets thrown if the expected stream version mismatches with the given original stream version
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static Task StoreAggregate(
- this IEventWriter eventWriter,
- TAggregate aggregate,
- StreamNameMap? streamNameMap = null,
- AmendEvent? amendEvent = null,
- CancellationToken cancellationToken = default
- ) where TAggregate : Aggregate where TState : State, new() where TId : Id {
- Ensure.NotNull(aggregate);
-
- var streamName = streamNameMap?.GetStreamName(aggregate.State.Id) ??
- StreamNameFactory.For(aggregate.State.Id);
-
- return eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken);
+ ///
+ /// Store aggregate changes to the event store
+ ///
+ /// Aggregate instance
+ /// Optional: stream name map
+ /// Optional: function to add extra information to the event before it gets stored
+ /// Cancellation token
+ /// Aggregate type
+ /// Aggregate state type
+ /// Aggregate identity type
+ /// Append event result
+ /// Gets thrown if the expected stream version mismatches with the given original stream version
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public Task StoreAggregate(
+ TAggregate aggregate,
+ StreamNameMap? streamNameMap = null,
+ AmendEvent? amendEvent = null,
+ CancellationToken cancellationToken = default
+ ) where TAggregate : Aggregate where TState : State, new() where TId : Id {
+ Ensure.NotNull(aggregate);
+
+ var streamName = streamNameMap?.GetStreamName(aggregate.State.Id) ??
+ StreamNameFactory.For(aggregate.State.Id);
+
+ return eventWriter.Store(streamName, new(aggregate.OriginalVersion), aggregate.Changes, amendEvent, cancellationToken);
+ }
}
- ///
- /// Loads aggregate from event store
- ///
/// Event reader or store
- /// Name of the aggregate stream
- /// Either fail if the stream is not found, default is false
- /// Optional: aggregate factory registry. Default instance will be used if the argument isn't provided.
- /// Cancellation token
- /// Aggregate type
- /// Aggregate state type
- /// Aggregate instance
- /// If failIfNotFound set to true, this exception is thrown if there's no stream
- ///
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(
- this IEventReader eventReader,
- StreamName streamName,
- bool failIfNotFound = true,
- AggregateFactoryRegistry? factoryRegistry = null,
- CancellationToken cancellationToken = default
- )
- where TAggregate : Aggregate where TState : State, new() {
- var aggregate = (factoryRegistry ?? AggregateFactoryRegistry.Instance).CreateInstance();
-
- try {
- var events = await eventReader.ReadStream(streamName, StreamReadPosition.Start, failIfNotFound, cancellationToken).NoContext();
- aggregate.Load(events.Select(x => x.Payload));
- } catch (StreamNotFound) when (!failIfNotFound) {
- return aggregate;
- } catch (Exception e) {
- Log.UnableToLoadAggregate(streamName, e);
+ extension(IEventReader eventReader) {
+ ///
+ /// Loads aggregate from event store
+ ///
+ /// Name of the aggregate stream
+ /// Either fail if the stream is not found, default is false
+ /// Optional: aggregate factory registry. Default instance will be used if the argument isn't provided.
+ /// Cancellation token
+ /// Aggregate type
+ /// Aggregate state type
+ /// Aggregate instance
+ /// If failIfNotFound set to true, this exception is thrown if there's no stream
+ ///
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(
+ StreamName streamName,
+ bool failIfNotFound = true,
+ AggregateFactoryRegistry? factoryRegistry = null,
+ CancellationToken cancellationToken = default
+ )
+ where TAggregate : Aggregate where TState : State, new() {
+ var aggregate = (factoryRegistry ?? AggregateFactoryRegistry.Instance).CreateInstance();
+
+ try {
+ var events = await eventReader.ReadStream(streamName, StreamReadPosition.Start, failIfNotFound, cancellationToken).NoContext();
+ aggregate.Load(events.Select(x => x.Payload));
+ } catch (StreamNotFound) when (!failIfNotFound) {
+ return aggregate;
+ } catch (Exception e) {
+ Log.UnableToLoadAggregate(streamName, e);
+
+ throw e is StreamNotFound ? new AggregateNotFoundException(streamName, e) : e;
+ }
- throw e is StreamNotFound ? new AggregateNotFoundException(streamName, e) : e;
+ return aggregate;
}
- return aggregate;
- }
-
- ///
- /// Loads aggregate from event store
- ///
- /// Event reader or store
- /// Aggregate identity
- /// Optional: stream name map. Default instance is used when argument isn't provided.
- /// Either fail if the stream is not found, default is false
- /// Optional: aggregate factory registry. Default instance will be used if the argument isn't provided.
- /// Cancellation token
- /// Aggregate type
- /// Aggregate state type
- /// Aggregate identity type
- /// Aggregate instance
- /// If failIfNotFound set to true, this exception is thrown if there's no stream
- ///
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(
- this IEventReader eventReader,
- TId aggregateId,
- StreamNameMap? streamNameMap = null,
- bool failIfNotFound = true,
- AggregateFactoryRegistry? factoryRegistry = null,
- CancellationToken cancellationToken = default
- )
- where TAggregate : Aggregate where TState : State, new() where TId : Id {
- var streamName = streamNameMap?.GetStreamName(aggregateId)
- ?? StreamNameFactory.For(aggregateId);
- var aggregate = await eventReader.LoadAggregate(streamName, failIfNotFound, factoryRegistry, cancellationToken).NoContext();
-
- return aggregate.WithId(aggregateId);
+ ///
+ /// Loads aggregate from event store
+ ///
+ /// Aggregate identity
+ /// Optional: stream name map. Default instance is used when argument isn't provided.
+ /// Either fail if the stream is not found, default is false
+ /// Optional: aggregate factory registry. Default instance will be used if the argument isn't provided.
+ /// Cancellation token
+ /// Aggregate type
+ /// Aggregate state type
+ /// Aggregate identity type
+ /// Aggregate instance
+ /// If failIfNotFound set to true, this exception is thrown if there's no stream
+ ///
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(
+ TId aggregateId,
+ StreamNameMap? streamNameMap = null,
+ bool failIfNotFound = true,
+ AggregateFactoryRegistry? factoryRegistry = null,
+ CancellationToken cancellationToken = default
+ )
+ where TAggregate : Aggregate where TState : State, new() where TId : Id {
+ var streamName = streamNameMap?.GetStreamName(aggregateId)
+ ?? StreamNameFactory.For(aggregateId);
+ var aggregate = await eventReader.LoadAggregate(streamName, failIfNotFound, factoryRegistry, cancellationToken).NoContext();
+
+ return aggregate.WithId(aggregateId);
+ }
}
}
diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs
index 1789cdb79..296b78582 100644
--- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs
+++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs
@@ -4,52 +4,53 @@
namespace Eventuous;
public static class AggregateStoreExtensions {
- ///
- /// Loads an aggregate by its ID, assigns the State.Id property
- ///
/// Aggregate store instance
- /// Stream name map
- /// Aggregate id
- ///
- /// Aggregate type
- /// State type
- /// Aggregate id type
- ///
- [Obsolete("Use IEventReader.LoadAggregates instead.")]
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task Load<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, TState, TId>(this IAggregateStore store, StreamNameMap streamNameMap, TId id, CancellationToken cancellationToken)
- where T : Aggregate where TId : Id where TState : State, new() {
- var aggregate = await store.Load(streamNameMap.GetStreamName(id), cancellationToken).NoContext();
+ extension(IAggregateStore store) {
+ ///
+ /// Loads an aggregate by its ID, assigns the State.Id property
+ ///
+ /// Stream name map
+ /// Aggregate id
+ ///
+ /// Aggregate type
+ /// State type
+ /// Aggregate id type
+ ///
+ [Obsolete("Use IEventReader.LoadAggregates instead.")]
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task Load
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, TState, TId>(StreamNameMap streamNameMap, TId id, CancellationToken cancellationToken)
+ where T : Aggregate where TId : Id where TState : State, new() {
+ var aggregate = await store.Load(streamNameMap.GetStreamName(id), cancellationToken).NoContext();
- return aggregate.WithId(id);
- }
+ return aggregate.WithId(id);
+ }
- ///
- /// Loads an aggregate by its ID, assigns the State.Id property.
- /// If the aggregate stream is not found, returns a new aggregate instance
- ///
- /// Aggregate store instance
- /// Stream name map
- /// Aggregate id
- ///
- /// Aggregate type
- /// State type
- /// Aggregate id type
- ///
- [Obsolete("Use IEventReader.LoadAggregates instead.")]
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(
- this IAggregateStore store,
- StreamNameMap streamNameMap,
- TId id,
- CancellationToken cancellationToken
- )
- where TAggregate : Aggregate where TId : Id where TState : State, new() {
- var aggregate = await store.LoadOrNew(streamNameMap.GetStreamName(id), cancellationToken).NoContext();
+ ///
+ /// Loads an aggregate by its ID, assigns the State.Id property.
+ /// If the aggregate stream is not found, returns a new aggregate instance
+ ///
+ /// Stream name map
+ /// Aggregate id
+ ///
+ /// Aggregate type
+ /// State type
+ /// Aggregate id type
+ ///
+ [Obsolete("Use IEventReader.LoadAggregates instead.")]
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(
+ StreamNameMap streamNameMap,
+ TId id,
+ CancellationToken cancellationToken
+ )
+ where TAggregate : Aggregate where TId : Id where TState : State, new() {
+ var aggregate = await store.LoadOrNew(streamNameMap.GetStreamName(id), cancellationToken).NoContext();
- return aggregate.WithId(id);
+ return aggregate.WithId(id);
+ }
}
internal static TAggregate WithId(this TAggregate aggregate, TId id)
diff --git a/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs b/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs
index 7e7898259..06fe26fcc 100644
--- a/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs
+++ b/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs
@@ -6,63 +6,62 @@ namespace Eventuous;
using static Diagnostics.PersistenceEventSource;
public static class StateStoreFunctions {
- ///
- /// Reads the event stream and folds it into a state object. This function will fail if the stream does not exist.
- ///
/// Event reader or event store
- /// Name of the stream to read from
- /// When set to false and there's no stream, the function will return an empty instance.
- /// Cancellation token
- /// State object type
- /// Instance of containing events and folded state
- /// Thrown if there's no stream and failIfNotFound is true
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task> LoadState(
- this IEventReader reader,
- StreamName streamName,
- bool failIfNotFound = true,
- CancellationToken cancellationToken = default
- ) where TState : State, new() {
- try {
- var streamEvents = await reader.ReadStream(streamName, StreamReadPosition.Start, failIfNotFound, cancellationToken).NoContext();
- var events = streamEvents.Select(x => x.Payload!).ToArray();
- var expectedVersion = events.Length == 0 ? ExpectedStreamVersion.NoStream : new(streamEvents.Last().Position);
+ extension(IEventReader reader) {
+ ///
+ /// Reads the event stream and folds it into a state object. This function will fail if the stream does not exist.
+ ///
+ /// Name of the stream to read from
+ /// When set to false and there's no stream, the function will return an empty instance.
+ /// Cancellation token
+ /// State object type
+ /// Instance of containing events and folded state
+ /// Thrown if there's no stream and failIfNotFound is true
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task> LoadState(
+ StreamName streamName,
+ bool failIfNotFound = true,
+ CancellationToken cancellationToken = default
+ ) where TState : State, new() {
+ try {
+ var streamEvents = await reader.ReadStream(streamName, StreamReadPosition.Start, failIfNotFound, cancellationToken).NoContext();
+ var events = streamEvents.Select(x => x.Payload!).ToArray();
+ var expectedVersion = events.Length == 0 ? ExpectedStreamVersion.NoStream : new(streamEvents.Last().Position);
- return (new(streamName, expectedVersion, events));
- } catch (StreamNotFound) when (!failIfNotFound) {
- return new(streamName, ExpectedStreamVersion.NoStream, []);
- } catch (Exception e) {
- Log.UnableToLoadStream(streamName, e);
+ return (new(streamName, expectedVersion, events));
+ } catch (StreamNotFound) when (!failIfNotFound) {
+ return new(streamName, ExpectedStreamVersion.NoStream, []);
+ } catch (Exception e) {
+ Log.UnableToLoadStream(streamName, e);
- throw;
+ throw;
+ }
}
- }
- ///
- /// Reads the event stream and folds it into a state object. This function will fail if the stream does not exist.
- ///
- /// Event reader or event store
- /// State identity value
- /// When set to false and there's no stream, the function will return an empty instance.
- /// Cancellation token
- /// Mapper between identity and stream name
- /// State object type
- /// State identity type
- /// Instance of containing events and folded state
- [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
- [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
- public static async Task> LoadState(
- this IEventReader reader,
- StreamNameMap streamNameMap,
- TId id,
- bool failIfNotFound = true,
- CancellationToken cancellationToken = default
- )
- where TState : State, new() where TId : Id {
- var foldedStream = await reader.LoadState(streamNameMap.GetStreamName(id), failIfNotFound, cancellationToken).NoContext();
+ ///
+ /// Reads the event stream and folds it into a state object. This function will fail if the stream does not exist.
+ ///
+ /// State identity value
+ /// When set to false and there's no stream, the function will return an empty instance.
+ /// Cancellation token
+ /// Mapper between identity and stream name
+ /// State object type
+ /// State identity type
+ /// Instance of containing events and folded state
+ [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)]
+ [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)]
+ public async Task> LoadState(
+ StreamNameMap streamNameMap,
+ TId id,
+ bool failIfNotFound = true,
+ CancellationToken cancellationToken = default
+ )
+ where TState : State, new() where TId : Id {
+ var foldedStream = await reader.LoadState(streamNameMap.GetStreamName(id), failIfNotFound, cancellationToken).NoContext();
- return foldedStream with { State = foldedStream.State.WithId(id) };
+ return foldedStream with { State = foldedStream.State.WithId(id) };
+ }
}
static TState WithId(this TState state, TId id) where TState : State, new() where TId : Id {
diff --git a/src/Core/src/Eventuous.Producers/ProducerExtensions.cs b/src/Core/src/Eventuous.Producers/ProducerExtensions.cs
index 00d0743cc..9e042dd65 100644
--- a/src/Core/src/Eventuous.Producers/ProducerExtensions.cs
+++ b/src/Core/src/Eventuous.Producers/ProducerExtensions.cs
@@ -91,5 +91,5 @@ static ProducedMessage[] ConvertOne(
AcknowledgeProduce? onAck,
ReportFailedProduce? onNack
)
- => [new ProducedMessage(message, metadata, additionalHeaders) { OnAck = onAck, OnNack = onNack }];
+ => [new(message, metadata, additionalHeaders) { OnAck = onAck, OnNack = onNack }];
}
diff --git a/src/Core/src/Eventuous.Producers/RegistrationExtensions.cs b/src/Core/src/Eventuous.Producers/RegistrationExtensions.cs
index 98e75537c..2ccd2aa50 100644
--- a/src/Core/src/Eventuous.Producers/RegistrationExtensions.cs
+++ b/src/Core/src/Eventuous.Producers/RegistrationExtensions.cs
@@ -12,59 +12,59 @@ namespace Microsoft.Extensions.DependencyInjection;
[PublicAPI]
public static class RegistrationExtensions {
- [Obsolete("Use AddProducer instead")]
- public static void AddEventProducer(this IServiceCollection services, T producer) where T : class, IProducer {
- services.AddProducer(producer);
- }
-
- ///
- /// Register a producer in the DI container as IProducer using a pre-instantiated instance.
- ///
///
- /// Producer instance
- /// Producer implementation type
- public static void AddProducer(this IServiceCollection services, T producer) where T : class, IProducer {
- services.TryAddSingleton(producer);
- services.TryAddSingleton(sp => sp.GetRequiredService());
+ extension(IServiceCollection services) {
+ [Obsolete("Use AddProducer instead")]
+ public void AddEventProducer(T producer) where T : class, IProducer {
+ services.AddProducer(producer);
+ }
- if (producer is IHostedService service) {
- services.TryAddSingleton(service);
+ ///
+ /// Register a producer in the DI container as IProducer using a pre-instantiated instance.
+ ///
+ /// Producer instance
+ /// Producer implementation type
+ public void AddProducer(T producer) where T : class, IProducer {
+ services.TryAddSingleton(producer);
+ services.TryAddSingleton(sp => sp.GetRequiredService());
+
+ if (producer is IHostedService service) {
+ services.TryAddSingleton(service);
+ }
}
- }
- [Obsolete("Use AddProducer instead")]
- public static void AddEventProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(this IServiceCollection services, Func getProducer)
- where T : class, IProducer {
- services.AddProducer(getProducer);
- }
+ [Obsolete("Use AddProducer instead")]
+ public void AddEventProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(Func getProducer)
+ where T : class, IProducer {
+ services.AddProducer(getProducer);
+ }
- ///
- /// Register a producer in the DI container as IProducer using a factory function.
- ///
- ///
- /// Function to resolve the producer from the service provider
- /// Producer implementation type
- public static void AddProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(this IServiceCollection services, Func getProducer)
- where T : class, IProducer {
- services.TryAddSingleton(getProducer);
- AddCommon(services);
- }
+ ///
+ /// Register a producer in the DI container as IProducer using a factory function.
+ ///
+ /// Function to resolve the producer from the service provider
+ /// Producer implementation type
+ public void AddProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(Func getProducer)
+ where T : class, IProducer {
+ services.TryAddSingleton(getProducer);
+ AddCommon(services);
+ }
- [Obsolete("Use AddProducer instead")]
- public static void AddEventProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>(this IServiceCollection services)
- where T : class, IProducer {
- services.AddProducer();
- }
+ [Obsolete("Use AddProducer instead")]
+ public void AddEventProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>()
+ where T : class, IProducer {
+ services.AddProducer();
+ }
- ///
- /// Register a producer in the DI container as IProducer using the default constructor.
- ///
- ///
- /// Producer implementation type
- public static void AddProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>(this IServiceCollection services)
- where T : class, IProducer {
- services.TryAddSingleton();
- AddCommon(services);
+ ///
+ /// Register a producer in the DI container as IProducer using the default constructor.
+ ///
+ /// Producer implementation type
+ public void AddProducer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>()
+ where T : class, IProducer {
+ services.TryAddSingleton();
+ AddCommon(services);
+ }
}
static void AddCommon<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(IServiceCollection services) where T : class, IProducer {
diff --git a/src/Core/src/Eventuous.Shared/Meta/MetadataExtensions.cs b/src/Core/src/Eventuous.Shared/Meta/MetadataExtensions.cs
index 78fc3952c..aa5e70214 100644
--- a/src/Core/src/Eventuous.Shared/Meta/MetadataExtensions.cs
+++ b/src/Core/src/Eventuous.Shared/Meta/MetadataExtensions.cs
@@ -4,33 +4,32 @@
namespace Eventuous;
public static class MetadataExtensions {
- ///
- /// Add correlation id to metadata
- ///
/// Metadata instance
- /// Correlation id value
- ///
- public static Metadata WithCorrelationId(this Metadata metadata, string? correlationId) => metadata.With(MetaTags.CorrelationId, correlationId);
+ extension(Metadata metadata) {
+ ///
+ /// Add correlation id to metadata
+ ///
+ /// Correlation id value
+ ///
+ public Metadata WithCorrelationId(string? correlationId) => metadata.With(MetaTags.CorrelationId, correlationId);
- ///
- /// Add causation id to metadata
- ///
- /// Metadata instance
- /// Causation id value
- ///
- public static Metadata WithCausationId(this Metadata metadata, string? causationId) => metadata.With(MetaTags.CausationId, causationId);
+ ///
+ /// Add causation id to metadata
+ ///
+ /// Causation id value
+ ///
+ public Metadata WithCausationId(string? causationId) => metadata.With(MetaTags.CausationId, causationId);
- ///
- /// Get the correlation id from metadata, if available
- ///
- /// Metadata instance
- /// Correlation id or null
- public static string? GetCorrelationId(this Metadata metadata) => metadata.GetString(MetaTags.CorrelationId);
+ ///
+ /// Get the correlation id from metadata, if available
+ ///
+ /// Correlation id or null
+ public string? GetCorrelationId() => metadata.GetString(MetaTags.CorrelationId);
- ///
- /// Get the causation id from metadata, if available
- ///
- /// Metadata instance
- /// Causation id or null
- public static string? GetCausationId(this Metadata metadata) => metadata.GetString(MetaTags.CausationId);
+ ///
+ /// Get the causation id from metadata, if available
+ ///
+ /// Causation id or null
+ public string? GetCausationId() => metadata.GetString(MetaTags.CausationId);
+ }
}
diff --git a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapperExtensions.cs b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapperExtensions.cs
index b68373cdd..b8e04beaa 100644
--- a/src/Core/src/Eventuous.Shared/TypeMap/TypeMapperExtensions.cs
+++ b/src/Core/src/Eventuous.Shared/TypeMap/TypeMapperExtensions.cs
@@ -6,54 +6,55 @@ namespace Eventuous;
using static TypeMapEventSource;
public static class TypeMapperExtensions {
- ///
- /// Get the type name for a given type
- ///
/// Type mapper instance
- /// Object type for which the name needs to be retrieved
- /// Indicates if exception should be thrown if the type is now registered
- /// Type name from the map or "unknown" if the type isn't registered and fail is set to false
- /// Thrown if the type isn't registered and fail is set to true
- public static string GetTypeNameByType(this ITypeMapper typeMapper, Type type, bool fail = true) {
- var typeKnown = typeMapper.TryGetTypeName(type, out var name);
-
- if (!typeKnown && fail) {
- Log.TypeNotMappedToName(type);
-
- throw new UnregisteredTypeException(type);
+ extension(ITypeMapper typeMapper) {
+ ///
+ /// Get the type name for a given type
+ ///
+ /// Object type for which the name needs to be retrieved
+ /// Indicates if exception should be thrown if the type is now registered
+ /// Type name from the map or "unknown" if the type isn't registered and fail is set to false
+ /// Thrown if the type isn't registered and fail is set to true
+ public string GetTypeNameByType(Type type, bool fail = true) {
+ var typeKnown = typeMapper.TryGetTypeName(type, out var name);
+
+ if (!typeKnown && fail) {
+ Log.TypeNotMappedToName(type);
+
+ throw new UnregisteredTypeException(type);
+ }
+
+ return name ?? ITypeMapper.UnknownType;
}
- return name ?? ITypeMapper.UnknownType;
- }
+ public string GetTypeName(object o, bool fail = true) => typeMapper.GetTypeNameByType(o.GetType(), fail);
- public static string GetTypeName(this ITypeMapper typeMapper, object o, bool fail = true) => typeMapper.GetTypeNameByType(o.GetType(), fail);
+ public string GetTypeName(bool fail = true) => typeMapper.GetTypeNameByType(typeof(T), fail);
- public static string GetTypeName(this ITypeMapper typeMapper, bool fail = true) => typeMapper.GetTypeNameByType(typeof(T), fail);
+ public bool TryGetTypeName([NotNullWhen(true)] out string? typeName) => typeMapper.TryGetTypeName(typeof(T), out typeName);
- public static bool TryGetTypeName(this ITypeMapper typeMapper, [NotNullWhen(true)] out string? typeName) => typeMapper.TryGetTypeName(typeof(T), out typeName);
+ ///
+ /// Get the registered type for a given name
+ ///
+ /// Type name for which the type needs to be returned
+ /// Type that matches the given name
+ /// Thrown if the type isn't registered and fail is set to true
+ public Type GetType(string typeName) {
+ var typeKnown = typeMapper.TryGetType(typeName, out var type);
- ///
- /// Get the registered type for a given name
- ///
- /// Type mapper instance
- /// Type name for which the type needs to be returned
- /// Type that matches the given name
- /// Thrown if the type isn't registered and fail is set to true
- public static Type GetType(this ITypeMapper typeMapper, string typeName) {
- var typeKnown = typeMapper.TryGetType(typeName, out var type);
+ if (!typeKnown) {
+ Log.TypeNameNotMappedToType(typeName);
- if (!typeKnown) {
- Log.TypeNameNotMappedToType(typeName);
+ throw new UnregisteredTypeException(typeName);
+ }
- throw new UnregisteredTypeException(typeName);
+ return type!;
}
- return type!;
- }
-
- public static void EnsureTypesRegistered(this ITypeMapper typeMapper, IEnumerable types) {
- foreach (var type in types) {
- typeMapper.GetTypeNameByType(type);
+ public void EnsureTypesRegistered(IEnumerable types) {
+ foreach (var type in types) {
+ typeMapper.GetTypeNameByType(type);
+ }
}
}
}
diff --git a/src/Core/src/Eventuous.Subscriptions/Channels/ChannelExtensions.cs b/src/Core/src/Eventuous.Subscriptions/Channels/ChannelExtensions.cs
index 1cb04fa04..871f78551 100644
--- a/src/Core/src/Eventuous.Subscriptions/Channels/ChannelExtensions.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Channels/ChannelExtensions.cs
@@ -9,56 +9,56 @@ namespace Eventuous.Subscriptions.Channels;
public delegate ValueTask ProcessElement(T element, CancellationToken cancellationToken);
static class ChannelExtensions {
- public static async Task Read(this Channel channel, ProcessElement process, CancellationToken cancellationToken) {
- try {
- while (!cancellationToken.IsCancellationRequested) {
- var element = await channel.Reader.ReadAsync(cancellationToken).NoContext();
- await process(element, cancellationToken).NoContext();
+ extension(Channel channel) {
+ public async Task Read(ProcessElement process, CancellationToken cancellationToken) {
+ try {
+ while (!cancellationToken.IsCancellationRequested) {
+ var element = await channel.Reader.ReadAsync(cancellationToken).NoContext();
+ await process(element, cancellationToken).NoContext();
+ }
+ } catch (OperationCanceledException) {
+ // it's ok
+ } catch (ChannelClosedException) {
+ // ok, we are quitting
}
- } catch (OperationCanceledException) {
- // it's ok
- } catch (ChannelClosedException) {
- // ok, we are quitting
}
- }
- public static async Task ReadBatches(
- this Channel channel,
- ProcessElement process,
- int maxCount,
- TimeSpan maxTime,
- CancellationToken cancellationToken
- ) {
- await foreach (var batch in channel.Reader.ReadAllBatches(maxCount, maxTime, cancellationToken).NoContext(cancellationToken)) {
- await process(batch, cancellationToken).NoContext();
+ public async Task ReadBatches(
+ ProcessElement process,
+ int maxCount,
+ TimeSpan maxTime,
+ CancellationToken cancellationToken
+ ) {
+ await foreach (var batch in channel.Reader.ReadAllBatches(maxCount, maxTime, cancellationToken).NoContext(cancellationToken)) {
+ await process(batch, cancellationToken).NoContext();
+ }
}
- }
- public static ValueTask Write(this Channel channel, T element, bool throwOnFull, CancellationToken cancellationToken) {
- return throwOnFull ? WriteOrThrow() : channel.Writer.WriteAsync(element, cancellationToken);
+ public ValueTask Write(T element, bool throwOnFull, CancellationToken cancellationToken) {
+ return throwOnFull ? WriteOrThrow() : channel.Writer.WriteAsync(element, cancellationToken);
- ValueTask WriteOrThrow() => !channel.Writer.TryWrite(element) ? throw new ChannelFullException() : default;
- }
+ ValueTask WriteOrThrow() => !channel.Writer.TryWrite(element) ? throw new ChannelFullException() : default;
+ }
- public static async ValueTask Stop(
- this Channel channel,
- CancellationTokenSource cts,
- Task[] readers,
- Func? finalize = null
- ) {
- channel.Writer.TryComplete();
+ public async ValueTask Stop(
+ CancellationTokenSource cts,
+ Task[] readers,
+ Func? finalize = null
+ ) {
+ channel.Writer.TryComplete();
- var incompleteReaders = readers.Where(r => !r.IsCompleted).ToArray();
+ var incompleteReaders = readers.Where(r => !r.IsCompleted).ToArray();
- if (readers.Length > 0) {
- cts.CancelAfter(TimeSpan.FromSeconds(10));
- await Task.WhenAll(incompleteReaders).NoContext();
- }
+ if (readers.Length > 0) {
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ await Task.WhenAll(incompleteReaders).NoContext();
+ }
- if (finalize == null) return;
+ if (finalize == null) return;
- using var ts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
- await finalize(ts.Token).NoContext();
+ using var ts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ await finalize(ts.Token).NoContext();
+ }
}
static async IAsyncEnumerable ReadAllBatches(
diff --git a/src/Core/src/Eventuous.Subscriptions/Context/ContextResultExtensions.cs b/src/Core/src/Eventuous.Subscriptions/Context/ContextResultExtensions.cs
index b49dbb5b4..a30790e6e 100644
--- a/src/Core/src/Eventuous.Subscriptions/Context/ContextResultExtensions.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Context/ContextResultExtensions.cs
@@ -10,91 +10,86 @@ namespace Eventuous.Subscriptions.Context;
using Logging;
public static class ContextResultExtensions {
- ///
- /// Allows acknowledging the message by a specific handler, identified by a string
- ///
/// Consume context
- /// Handler type identifier
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Ack(this IBaseConsumeContext context, string handlerType) {
- context.HandlingResults.Add(EventHandlingResult.Succeeded(handlerType));
- context.LogContext.MessageHandled(handlerType, context);
- }
+ extension(IBaseConsumeContext context) {
+ ///
+ /// Allows acknowledging the message by a specific handler, identified by a string
+ ///
+ /// Handler type identifier
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Ack(string handlerType) {
+ context.HandlingResults.Add(EventHandlingResult.Succeeded(handlerType));
+ context.LogContext.MessageHandled(handlerType, context);
+ }
- ///
- /// Allows conveying the message handling failure that occurred in a specific handler
- ///
- /// Message context
- /// Handler type identifier
- /// Optional: handler exception
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Nack(this IBaseConsumeContext context, string handlerType, Exception? exception) {
- context.HandlingResults.Add(EventHandlingResult.Failed(handlerType, exception));
+ ///
+ /// Allows conveying the message handling failure that occurred in a specific handler
+ ///
+ /// Handler type identifier
+ /// Optional: handler exception
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Nack(string handlerType, Exception? exception) {
+ context.HandlingResults.Add(EventHandlingResult.Failed(handlerType, exception));
- context.LogContext.MessageHandlingFailed(handlerType, context, exception);
+ context.LogContext.MessageHandlingFailed(handlerType, context, exception);
- if (Activity.Current != null && Activity.Current.Status != ActivityStatusCode.Error) {
- Activity.Current.SetActivityStatus(
- ActivityStatus.Error(exception, $"Error handling {context.MessageType}")
- );
+ if (Activity.Current != null && Activity.Current.Status != ActivityStatusCode.Error) {
+ Activity.Current.SetActivityStatus(
+ ActivityStatus.Error(exception, $"Error handling {context.MessageType}")
+ );
+ }
}
- }
- ///
- /// Allows conveying the fact that the message was ignored by the handler
- ///
- /// Consume context
- /// Handler type identifier
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Ignore(this IBaseConsumeContext context, string handlerType) {
- context.HandlingResults.Add(EventHandlingResult.Ignored(handlerType));
- context.LogContext.MessageIgnored(handlerType, context);
- }
+ ///
+ /// Allows conveying the fact that the message was ignored by the handler
+ ///
+ /// Handler type identifier
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Ignore(string handlerType) {
+ context.HandlingResults.Add(EventHandlingResult.Ignored(handlerType));
+ context.LogContext.MessageIgnored(handlerType, context);
+ }
- ///
- /// Allows acknowledging the message by a specific handler, identified by a string
- ///
- /// Consume context
- /// Handler type
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Ack(this IBaseConsumeContext context) => context.Ack(typeof(T).Name);
+ ///
+ /// Allows acknowledging the message by a specific handler, identified by a string
+ ///
+ /// Handler type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Ack() => context.Ack(typeof(T).Name);
- ///
- /// Allows conveying the fact that the message was ignored by the handler
- ///
- /// Consume context
- /// Handler type
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Ignore(this IBaseConsumeContext context) => context.Ignore(typeof(T).Name);
+ ///
+ /// Allows conveying the fact that the message was ignored by the handler
+ ///
+ /// Handler type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Ignore() => context.Ignore(typeof(T).Name);
- ///
- /// Allows conveying the message handling failure that occurred in a specific handler
- ///
- /// Consume context
- /// Optional: handler exception
- /// Handler type
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Nack(this IBaseConsumeContext context, Exception? exception) => context.Nack(typeof(T).Name, exception);
+ ///
+ /// Allows conveying the message handling failure that occurred in a specific handler
+ ///
+ /// Optional: handler exception
+ /// Handler type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Nack(Exception? exception) => context.Nack(typeof(T).Name, exception);
- ///
- /// Returns true if the message was ignored by all handlers
- ///
- ///
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool WasIgnored(this IBaseConsumeContext context) {
- var status = context.HandlingResults.GetIgnoreStatus();
- var handleStatus = context.HandlingResults.GetFailureStatus();
+ ///
+ /// Returns true if the message was ignored by all handlers
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool WasIgnored() {
+ var status = context.HandlingResults.GetIgnoreStatus();
+ var handleStatus = context.HandlingResults.GetFailureStatus();
- return (status & EventHandlingStatus.Ignored) == EventHandlingStatus.Ignored && handleStatus == 0;
- }
+ return (status & EventHandlingStatus.Ignored) == EventHandlingStatus.Ignored && handleStatus == 0;
+ }
- ///
- /// Returns true if any of the handlers reported a failure
- ///
- /// Consume context
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool HasFailed(this IBaseConsumeContext context)
- => context.HandlingResults.GetFailureStatus() == EventHandlingStatus.Failure;
+ ///
+ /// Returns true if any of the handlers reported a failure
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool HasFailed()
+ => context.HandlingResults.GetFailureStatus() == EventHandlingStatus.Failure;
+ }
}
diff --git a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
index 4181135b6..6ab5d2108 100644
--- a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
+++ b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs
@@ -97,7 +97,7 @@ protected async Task GetCheckpoint(CancellationToken cancellationTok
LoggerFactory
);
- if (IsRunning && LastProcessed != null) { return new Checkpoint(Options.SubscriptionId, LastProcessed?.Position); }
+ if (IsRunning && LastProcessed != null) { return new(Options.SubscriptionId, LastProcessed?.Position); }
Logger.Current = Log;
diff --git a/src/Core/src/Eventuous.Subscriptions/Logging/CheckpointLogging.cs b/src/Core/src/Eventuous.Subscriptions/Logging/CheckpointLogging.cs
index 0cec36985..0dfdaa192 100644
--- a/src/Core/src/Eventuous.Subscriptions/Logging/CheckpointLogging.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Logging/CheckpointLogging.cs
@@ -10,27 +10,31 @@ namespace Eventuous.Subscriptions.Logging;
using Checkpoints;
public static class CheckpointLogging {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void PositionReceived(this LogContext log, CommitPosition checkpoint)
- => log.TraceLog?.Log("Received checkpoint: {Position}", checkpoint);
+ extension(LogContext log) {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PositionReceived(CommitPosition checkpoint)
+ => log.TraceLog?.Log("Received checkpoint: {Position}", checkpoint);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CommittingPosition(this LogContext log, CommitPosition position)
- => log.DebugLog?.Log("Committing position {Position}", position);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CommittingPosition(CommitPosition position)
+ => log.DebugLog?.Log("Committing position {Position}", position);
- public static void UnableToCommitPosition(this LogContext log, CommitPosition position, Exception exception)
- => log.ErrorLog?.Log(exception, "Unable to commit position {Position}", position);
+ public void UnableToCommitPosition(CommitPosition position, Exception exception)
+ => log.ErrorLog?.Log(exception, "Unable to commit position {Position}", position);
+ }
- public static void CheckpointLoaded(this LogContext? log, ICheckpointStore store, Checkpoint checkpoint)
- => log?.InfoLog?.Log("Loaded checkpoint {CheckpointId} from {Store}: {Position}", checkpoint.Id, store.GetType().Name, checkpoint);
+ extension(LogContext? log) {
+ public void CheckpointLoaded(ICheckpointStore store, Checkpoint checkpoint)
+ => log?.InfoLog?.Log("Loaded checkpoint {CheckpointId} from {Store}: {Position}", checkpoint.Id, store.GetType().Name, checkpoint);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CheckpointStored(this LogContext? log, ICheckpointStore store, Checkpoint checkpoint, bool force) {
- if (log == null) return;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CheckpointStored(ICheckpointStore store, Checkpoint checkpoint, bool force) {
+ if (log == null) return;
- const string message = "Stored checkpoint {CheckpointId} in {Store}: {Position}";
+ const string message = "Stored checkpoint {CheckpointId} in {Store}: {Position}";
- if (force) log.InfoLog?.Log(message, checkpoint.Id, store.GetType().Name, checkpoint);
- else log.TraceLog?.Log(message, checkpoint.Id, store.GetType().Name, checkpoint);
+ if (force) log.InfoLog?.Log(message, checkpoint.Id, store.GetType().Name, checkpoint);
+ else log.TraceLog?.Log(message, checkpoint.Id, store.GetType().Name, checkpoint);
+ }
}
}
diff --git a/src/Core/src/Eventuous.Subscriptions/Logging/SubscriptionLogging.cs b/src/Core/src/Eventuous.Subscriptions/Logging/SubscriptionLogging.cs
index 741dac4bc..a46f141fb 100644
--- a/src/Core/src/Eventuous.Subscriptions/Logging/SubscriptionLogging.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Logging/SubscriptionLogging.cs
@@ -9,90 +9,88 @@ namespace Eventuous.Subscriptions.Logging;
using Context;
public static class LoggingExtensions {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void MessageReceived(this LogContext log, IMessageConsumeContext context)
- => log.TraceLog?.Log(
- "Received {MessageType} from {Stream}:{Position} seq {Sequence}",
- context.MessageType,
- context.Stream,
- context.GlobalPosition,
- context.Sequence
- );
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void MessageHandled(this LogContext log, string handlerType, IBaseConsumeContext context)
- => log.TraceLog?.Log(
- "{Handler} handled {MessageType} {Stream}:{Position} seq {Sequence}",
- handlerType,
- context.MessageType,
- context.Stream,
- context.GlobalPosition,
- context.Sequence
- );
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void MessageIgnored(this LogContext log, string handlerType, IBaseConsumeContext context)
- => log.TraceLog?.Log(
- "{Handler} ignored {MessageType} {Stream}:{Position} seq {Sequence}",
- handlerType,
- context.MessageType,
- context.Stream,
- context.GlobalPosition,
- context.Sequence
- );
-
- public static void MessageIgnoredWhenStopping(this LogContext log, Exception e) =>
- log.DebugLog?.Log("Message ignored because subscription is stopping: {Message}", e.Message);
+ extension(LogContext log) {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MessageReceived(IMessageConsumeContext context)
+ => log.TraceLog?.Log(
+ "Received {MessageType} from {Stream}:{Position} seq {Sequence}",
+ context.MessageType,
+ context.Stream,
+ context.GlobalPosition,
+ context.Sequence
+ );
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MessageHandled(string handlerType, IBaseConsumeContext context)
+ => log.TraceLog?.Log(
+ "{Handler} handled {MessageType} {Stream}:{Position} seq {Sequence}",
+ handlerType,
+ context.MessageType,
+ context.Stream,
+ context.GlobalPosition,
+ context.Sequence
+ );
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MessageIgnored(string handlerType, IBaseConsumeContext context)
+ => log.TraceLog?.Log(
+ "{Handler} ignored {MessageType} {Stream}:{Position} seq {Sequence}",
+ handlerType,
+ context.MessageType,
+ context.Stream,
+ context.GlobalPosition,
+ context.Sequence
+ );
+
+ public void MessageIgnoredWhenStopping(Exception e) =>
+ log.DebugLog?.Log("Message ignored because subscription is stopping: {Message}", e.Message);
+
+ public void MessageHandlerNotFound(string handler, string messageType)
+ => log.WarnLog?.Log("No handler found in {Handler} for message type {MessageType}", handler, messageType);
+
+ public void MessageHandlingFailed(string handlerType, IBaseConsumeContext context, Exception? exception)
+ => log.ErrorLog?.Log(exception, "Message handling failed at {HandlerType} for message {MessageId}", handlerType, context.MessageId);
+
+ public void PayloadDeserializationFailed(string stream, ulong position, string messageType, Exception exception)
+ => log.ErrorLog?.Log(exception, "Failed to deserialize event {MessageType} at {Stream}:{Position}", messageType, stream, position);
+
+ public void MetadataDeserializationFailed(string stream, ulong position, Exception exception)
+ => log.ErrorLog?.Log(exception, "Failed to deserialize metadata at {Stream}:{Position}", stream, position);
+
+ public void MessagePayloadInconclusive(string messageType, string stream, DeserializationError error)
+ => log.DebugLog?.Log("Message of type {MessageType} from {Stream} ignored as it didn't deserialize: {Error}", messageType, stream, error);
+
+ public void ThrowOnErrorIncompatible()
+ => log.WarnLog?.Log("Failure handler is set, but ThrowOnError is disabled, so the failure handler will never be called");
+
+ public void FailedToHandleMessageWithRetry(string handlerType, string messageType, int retryCount, Exception exception)
+ => log.ErrorLog?.Log(
+ exception,
+ "Failed to handle message {MessageType} with {HandlerType} after {RetryCount} retries",
+ messageType,
+ handlerType,
+ retryCount
+ );
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MessageAcked(string messageType, ulong position)
+ => log.TraceLog?.Log("Message {Type} acknowledged at {Position}", messageType, position);
+
+ public void MessageNacked(string messageType, ulong position, Exception exception)
+ => log.WarnLog?.Log(exception, "Message {Type} not acknowledged at {Position}", messageType, position);
+
+ public void SubscriptionStarted() => log.InfoLog?.Log("Started");
+ public void SubscriptionStopped() => log.InfoLog?.Log("Stopped");
+
+ public void SubscriptionDropped(DropReason reason, Exception? exception)
+ => log.WarnLog?.Log(exception, "Dropped: {Reason}", reason);
+
+ public void SubscriptionWillResubscribe(TimeSpan delay) => log.WarnLog?.Log($"Will resubscribe after {delay}");
+ public void SubscriptionResubscribing() => log.WarnLog?.Log("Resubscribing");
+ public void SubscriptionResubscribed() => log.InfoLog?.Log("Resubscribed");
+ public void SubscriptionResubscribeFailed(Exception e) => log.ErrorLog?.Log(e, "Failed to resubscribe");
+ }
public static void MessageTypeNotFound(this ILogger? log)
=> log?.LogWarning("Message type {MessageType} not registered in the type map", typeof(T).Name);
-
- public static void MessageHandlerNotFound(this LogContext log, string handler, string messageType)
- => log.WarnLog?.Log("No handler found in {Handler} for message type {MessageType}", handler, messageType);
-
- public static void MessageHandlingFailed(this LogContext log, string handlerType, IBaseConsumeContext context, Exception? exception)
- => log.ErrorLog?.Log(exception, "Message handling failed at {HandlerType} for message {MessageId}", handlerType, context.MessageId);
-
- public static void PayloadDeserializationFailed(this LogContext log, string stream, ulong position, string messageType, Exception exception)
- => log.ErrorLog?.Log(exception, "Failed to deserialize event {MessageType} at {Stream}:{Position}", messageType, stream, position);
-
- public static void MetadataDeserializationFailed(this LogContext log, string stream, ulong position, Exception exception)
- => log.ErrorLog?.Log(exception, "Failed to deserialize metadata at {Stream}:{Position}", stream, position);
-
- public static void MessagePayloadInconclusive(this LogContext log, string messageType, string stream, DeserializationError error)
- => log.DebugLog?.Log("Message of type {MessageType} from {Stream} ignored as it didn't deserialize: {Error}", messageType, stream, error);
-
- public static void ThrowOnErrorIncompatible(this LogContext log)
- => log.WarnLog?.Log("Failure handler is set, but ThrowOnError is disabled, so the failure handler will never be called");
-
- public static void FailedToHandleMessageWithRetry(this LogContext log, string handlerType, string messageType, int retryCount, Exception exception)
- => log.ErrorLog?.Log(
- exception,
- "Failed to handle message {MessageType} with {HandlerType} after {RetryCount} retries",
- messageType,
- handlerType,
- retryCount
- );
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void MessageAcked(this LogContext log, string messageType, ulong position)
- => log.TraceLog?.Log("Message {Type} acknowledged at {Position}", messageType, position);
-
- public static void MessageNacked(this LogContext log, string messageType, ulong position, Exception exception)
- => log.WarnLog?.Log(exception, "Message {Type} not acknowledged at {Position}", messageType, position);
-
- public static void SubscriptionStarted(this LogContext log) => log.InfoLog?.Log("Started");
-
- public static void SubscriptionStopped(this LogContext log) => log.InfoLog?.Log("Stopped");
-
- public static void SubscriptionDropped(this LogContext log, DropReason reason, Exception? exception)
- => log.WarnLog?.Log(exception, "Dropped: {Reason}", reason);
-
- public static void SubscriptionWillResubscribe(this LogContext log, TimeSpan delay) => log.WarnLog?.Log($"Will resubscribe after {delay}");
-
- public static void SubscriptionResubscribing(this LogContext log) => log.WarnLog?.Log("Resubscribing");
-
- public static void SubscriptionResubscribed(this LogContext log) => log.InfoLog?.Log("Resubscribed");
-
- public static void SubscriptionResubscribeFailed(this LogContext log, Exception e) => log.ErrorLog?.Log(e, "Failed to resubscribe");
}
diff --git a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
index a04f40a77..37b0c0d57 100644
--- a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
@@ -232,7 +232,7 @@ IMessageConsumer ResolveDefaultConsumer(IServiceProvider sp) {
/// Resolves and builds the subscription instance of type .
/// Applies tracing and consumer filters to the consume pipe when diagnostics are enabled,
/// resolves the configured consumer, and creates the subscription using options keyed by
- /// .
+ /// SubscriptionId.
///
/// Service provider used to resolve dependencies
/// The resolved and configured subscription instance
diff --git a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs
index e42ca23de..eb4fd15fa 100644
--- a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilderExtensions.cs
@@ -13,108 +13,104 @@ namespace Eventuous.Subscriptions.Registrations;
using System.Diagnostics.CodeAnalysis;
public static class SubscriptionBuilderExtensions {
- ///
- /// Adds partitioning to the subscription. Keep in mind that not all subscriptions can support partitioned consume.
- ///
/// Subscription builder
- /// Number of partitions
- /// Function to get the partition key from the context
- ///
- [PublicAPI]
- public static SubscriptionBuilder WithPartitioning(this SubscriptionBuilder builder, int partitionsCount, Partitioner.GetPartitionKey getPartitionKey)
- => builder.AddConsumeFilterFirst(new PartitioningFilter(partitionsCount, getPartitionKey));
-
- ///
- /// Adds partitioning to the subscription using the stream name as partition key.
- /// Keep in mind that not all subscriptions can support partitioned consume.
- ///
- /// Subscription builder
- /// Number of partitions
- ///
- [PublicAPI]
- public static SubscriptionBuilder WithPartitioningByStream(this SubscriptionBuilder builder, int partitionsCount)
- => builder.WithPartitioning(partitionsCount, ctx => ctx.Stream);
-
- ///
- /// Use non-default checkpoint store for the specific subscription
- ///
- /// Subscription builder
- /// Checkpoint store type
- ///
- public static SubscriptionBuilder UseCheckpointStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this SubscriptionBuilder builder)
- where T : class, ICheckpointStore {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
-
- if (EventuousDiagnostics.Enabled) {
- builder.Services.TryAddKeyedSingleton(
- builder.SubscriptionId,
- (sp, key) => new MeasuredCheckpointStore(sp.GetRequiredKeyedService(key))
- );
- }
- else {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
+ extension(SubscriptionBuilder builder) {
+ ///
+ /// Adds partitioning to the subscription. Keep in mind that not all subscriptions can support partitioned consume.
+ ///
+ /// Number of partitions
+ /// Function to get the partition key from the context
+ ///
+ [PublicAPI]
+ public SubscriptionBuilder WithPartitioning(int partitionsCount, Partitioner.GetPartitionKey getPartitionKey)
+ => builder.AddConsumeFilterFirst(new PartitioningFilter(partitionsCount, getPartitionKey));
+
+ ///
+ /// Adds partitioning to the subscription using the stream name as partition key.
+ /// Keep in mind that not all subscriptions can support partitioned consume.
+ ///
+ /// Number of partitions
+ ///
+ [PublicAPI]
+ public SubscriptionBuilder WithPartitioningByStream(int partitionsCount)
+ => builder.WithPartitioning(partitionsCount, ctx => ctx.Stream);
+
+ ///
+ /// Use non-default checkpoint store for the specific subscription
+ ///
+ /// Checkpoint store type
+ ///
+ public SubscriptionBuilder UseCheckpointStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
+ where T : class, ICheckpointStore {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
+
+ if (EventuousDiagnostics.Enabled) {
+ builder.Services.TryAddKeyedSingleton(
+ builder.SubscriptionId,
+ (sp, key) => new MeasuredCheckpointStore(sp.GetRequiredKeyedService(key))
+ );
+ }
+ else {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
+ }
+
+ return builder;
}
- return builder;
- }
-
- ///
- /// Use non-default checkpoint store for the specific subscription
- ///
- /// Subscription builder
- /// Function to resolve the checkpoint store service from service provider
- /// Checkpoint store type
- ///
- public static SubscriptionBuilder UseCheckpointStore(this SubscriptionBuilder builder, Func factory)
- where T : class, ICheckpointStore {
- if (EventuousDiagnostics.Enabled) {
- builder.Services.TryAddKeyedSingleton(
- builder.SubscriptionId,
- (sp, _) => new MeasuredCheckpointStore(factory(sp))
- );
- }
- else {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, (sp, _) => factory(sp));
+ ///
+ /// Use non-default checkpoint store for the specific subscription
+ ///
+ /// Function to resolve the checkpoint store service from service provider
+ /// Checkpoint store type
+ ///
+ public SubscriptionBuilder UseCheckpointStore(Func factory)
+ where T : class, ICheckpointStore {
+ if (EventuousDiagnostics.Enabled) {
+ builder.Services.TryAddKeyedSingleton(
+ builder.SubscriptionId,
+ (sp, _) => new MeasuredCheckpointStore(factory(sp))
+ );
+ }
+ else {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, (sp, _) => factory(sp));
+ }
+
+ return builder;
}
- return builder;
- }
+ ///
+ /// Use non-default serializer for the specific subscription
+ ///
+ /// Function to create the serializer instance
+ /// Serializer type
+ ///
+ public SubscriptionBuilder UseSerializer(Func factory) where T : class, IEventSerializer {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, (sp, _) => factory(sp));
- ///
- /// Use non-default serializer for the specific subscription
- ///
- /// Subscription builder
- /// Function to create the serializer instance
- /// Serializer type
- ///
- public static SubscriptionBuilder UseSerializer(this SubscriptionBuilder builder, Func factory) where T : class, IEventSerializer {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, (sp, _) => factory(sp));
-
- return builder;
- }
+ return builder;
+ }
- ///
- /// Use non-default serializer for the specific subscription
- ///
- /// Subscription builder
- /// Serializer type
- ///
- public static SubscriptionBuilder UseSerializer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this SubscriptionBuilder builder) where T : class, IEventSerializer {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
+ ///
+ /// Use non-default serializer for the specific subscription
+ ///
+ /// Serializer type
+ ///
+ public SubscriptionBuilder UseSerializer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class, IEventSerializer {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId);
- return builder;
- }
+ return builder;
+ }
- ///
- /// Use non-default type mapper for the specific subscription
- ///
- /// Subscription builder
- /// Custom type mapper instance
- /// Type mapper type
- ///
- public static SubscriptionBuilder UseTypeMapper(this SubscriptionBuilder builder, T typeMapper) where T : class, ITypeMapper {
- builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, typeMapper);
+ ///
+ /// Use non-default type mapper for the specific subscription
+ ///
+ /// Custom type mapper instance
+ /// Type mapper type
+ ///
+ public SubscriptionBuilder UseTypeMapper(T typeMapper) where T : class, ITypeMapper {
+ builder.Services.TryAddKeyedSingleton(builder.SubscriptionId, typeMapper);
- return builder;
+ return builder;
+ }
}
}
diff --git a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionRegistrationExtensions.cs b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionRegistrationExtensions.cs
index 707be3e48..fdf087a9f 100644
--- a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionRegistrationExtensions.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionRegistrationExtensions.cs
@@ -65,18 +65,20 @@ string[] tags
return builder.AddCheck(checkName, failureStatus, tags);
}
- public static IServiceCollection AddCheckpointStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services)
- where T : class, ICheckpointStore {
- services.AddSingleton();
+ extension(IServiceCollection services) {
+ public IServiceCollection AddCheckpointStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
+ where T : class, ICheckpointStore {
+ services.AddSingleton();
- return AddCheckpointStoreInternal(services);
- }
+ return AddCheckpointStoreInternal(services);
+ }
- public static IServiceCollection AddCheckpointStore(this IServiceCollection services, Func getStore)
- where T : class, ICheckpointStore {
- services.AddSingleton(getStore);
+ public IServiceCollection AddCheckpointStore(Func getStore)
+ where T : class, ICheckpointStore {
+ services.AddSingleton(getStore);
- return AddCheckpointStoreInternal(services);
+ return AddCheckpointStoreInternal(services);
+ }
}
static void TryAddSubscriptionHealthCheck(IServiceCollection services) {
diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs
index 891d60e78..71c0694aa 100644
--- a/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs
+++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Fixtures/Helpers.cs
@@ -16,35 +16,20 @@ public static IEnumerable CreateEvents(this StoreFixtureBase fi
static BookingImported ToEvent(ImportBooking cmd) => new(cmd.RoomId, cmd.Price, cmd.CheckIn, cmd.CheckOut);
- public static Task AppendEvents(
- this StoreFixtureBase fixture,
- StreamName stream,
- object[] evt,
- ExpectedStreamVersion version
- ) {
- var streamEvents = evt.Select(x => new NewStreamEvent(Guid.NewGuid(), x, new()));
-
- return fixture.EventStore.AppendEvents(stream, version, streamEvents.ToArray(), default);
- }
+ extension(StoreFixtureBase fixture) {
+ public Task AppendEvents(StreamName stream, object[] evt, ExpectedStreamVersion version) {
+ var streamEvents = evt.Select(x => new NewStreamEvent(Guid.NewGuid(), x, new()));
- public static Task AppendEvent(
- this StoreFixtureBase fixture,
- StreamName stream,
- object evt,
- ExpectedStreamVersion version,
- Metadata? metadata = null
- ) {
- var streamEvent = new NewStreamEvent(Guid.NewGuid(), evt, metadata ?? new Metadata());
+ return fixture.EventStore.AppendEvents(stream, version, streamEvents.ToArray(), default);
+ }
- return fixture.EventStore.AppendEvents(stream, version, [streamEvent], default);
- }
+ public Task AppendEvent(StreamName stream, object evt, ExpectedStreamVersion version, Metadata? metadata = null) {
+ var streamEvent = new NewStreamEvent(Guid.NewGuid(), evt, metadata ?? new Metadata());
+
+ return fixture.EventStore.AppendEvents(stream, version, [streamEvent], default);
+ }
- public static Task StoreChanges(
- this StoreFixtureBase fixture,
- StreamName stream,
- object evt,
- ExpectedStreamVersion version
- ) {
- return fixture.EventStore.Store(stream, version, [evt]);
+ public Task StoreChanges(StreamName stream, object evt, ExpectedStreamVersion version)
+ => fixture.EventStore.Store(stream, version, [evt]);
}
}
diff --git a/src/Core/test/Eventuous.Tests.Shared.Analyzers/Analyzer_Ev001_Tests.cs b/src/Core/test/Eventuous.Tests.Shared.Analyzers/Analyzer_Ev001_Tests.cs
index 310bca609..5eb62ed4a 100644
--- a/src/Core/test/Eventuous.Tests.Shared.Analyzers/Analyzer_Ev001_Tests.cs
+++ b/src/Core/test/Eventuous.Tests.Shared.Analyzers/Analyzer_Ev001_Tests.cs
@@ -53,7 +53,7 @@ static CSharpCompilation CreateCompilation(string source) {
MetadataReference.CreateFromFile(typeof(Enumerable).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(State<>).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Aggregate<>).Assembly.Location),
- MetadataReference.CreateFromFile(typeof(EventTypeAttribute).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(EventTypeAttribute).Assembly.Location)
};
// Some frameworks need additional facades depending on runtime; try to add them if present
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs
index 16edd47ae..6c1f6048c 100644
--- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs
+++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/SubscriptionExtensions.cs
@@ -4,16 +4,18 @@
namespace Eventuous.Tests.Subscriptions.Base;
public static class SubscriptionExtensions {
- public static ValueTask SubscribeWithLog(this IMessageSubscription subscription, ILogger log, CancellationToken cancellationToken = default)
- => subscription.Subscribe(
- id => log.LogInformation("{Subscription} subscribed", id),
- (id, reason, ex) => log.LogWarning(ex, "{Subscription} dropped {Reason}", id, reason),
- cancellationToken
- );
+ extension(IMessageSubscription subscription) {
+ public ValueTask SubscribeWithLog(ILogger log, CancellationToken cancellationToken = default)
+ => subscription.Subscribe(
+ id => log.LogInformation("{Subscription} subscribed", id),
+ (id, reason, ex) => log.LogWarning(ex, "{Subscription} dropped {Reason}", id, reason),
+ cancellationToken
+ );
- public static ValueTask UnsubscribeWithLog(this IMessageSubscription subscription, ILogger log, CancellationToken cancellationToken = default)
- => subscription.Unsubscribe(
- id => log.LogInformation("{Subscription} unsubscribed", id),
- cancellationToken
- );
+ public ValueTask UnsubscribeWithLog(ILogger log, CancellationToken cancellationToken = default)
+ => subscription.Unsubscribe(
+ id => log.LogInformation("{Subscription} unsubscribed", id),
+ cancellationToken
+ );
+ }
}
\ No newline at end of file
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs
index 916e0648e..8ef13f177 100644
--- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs
+++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Fixtures/TestEventHandler.cs
@@ -14,7 +14,7 @@ namespace Eventuous.Tests.Subscriptions.Base;
public record TestEvent(string Data, int Number) {
public const string TypeName = "test-event";
- static readonly Faker Faker = new Faker().CustomInstantiator(f => new TestEvent(f.Lorem.Sentence(), f.Random.Int()));
+ static readonly Faker Faker = new Faker().CustomInstantiator(f => new(f.Lorem.Sentence(), f.Random.Int()));
public static TestEvent Create() => Faker.Generate();
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs
index 8d17b766b..cc43beba1 100644
--- a/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs
+++ b/src/Core/test/Eventuous.Tests.Subscriptions/SequenceTests.cs
@@ -27,7 +27,7 @@ public void ShouldReturnFirstBefore(CommitPositionSequence sequence, CommitPosit
public void ShouldWorkForOne() {
var timestamp = DateTime.Now;
var sequence = new CommitPositionSequence { new(0, 1, timestamp) };
- sequence.FirstBeforeGap().ShouldBe(new CommitPosition(0, 1, timestamp));
+ sequence.FirstBeforeGap().ShouldBe(new(0, 1, timestamp));
}
[Test]
@@ -58,7 +58,7 @@ public void ShouldWorkForNormalCase() {
}
var first = sequence.FirstBeforeGap();
- first.ShouldBe(new CommitPosition(9, 9, timestamp));
+ first.ShouldBe(new(9, 9, timestamp));
}
public static IEnumerable> TestData() {
diff --git a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/MeterProviderBuilderExtensions.cs b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/MeterProviderBuilderExtensions.cs
index 74d61e268..8115d131a 100644
--- a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/MeterProviderBuilderExtensions.cs
+++ b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/MeterProviderBuilderExtensions.cs
@@ -10,45 +10,46 @@ namespace Eventuous.Diagnostics.OpenTelemetry;
[PublicAPI]
public static class MeterProviderBuilderExtensions {
- ///
- /// Adds subscription metrics instrumentation
- ///
///
- ///
- ///
- public static MeterProviderBuilder AddEventuousSubscriptions(this MeterProviderBuilder builder, TagList? customTags = null)
- => Ensure.NotNull(builder).AddMeter(SubscriptionMetrics.MeterName).AddMetrics(customTags);
+ extension(MeterProviderBuilder builder) {
+ ///
+ /// Adds subscription metrics instrumentation
+ ///
+ ///
+ ///
+ public MeterProviderBuilder AddEventuousSubscriptions(TagList? customTags = null)
+ => Ensure.NotNull(builder).AddMeter(SubscriptionMetrics.MeterName).AddMetrics(customTags);
- ///
- /// Adds metrics instrumentation for core components such as application service and event store
- ///
- ///
- ///
- ///
- public static MeterProviderBuilder AddEventuous(this MeterProviderBuilder builder, TagList? customTags = null)
- => Ensure.NotNull(builder)
- .AddMeter(CommandServiceMetrics.MeterName)
- .AddMetrics(customTags)
- .AddMeter(PersistenceMetrics.MeterName)
- .AddMetrics(customTags);
+ ///
+ /// Adds metrics instrumentation for core components such as application service and event store
+ ///
+ ///
+ ///
+ public MeterProviderBuilder AddEventuous(TagList? customTags = null)
+ => Ensure.NotNull(builder)
+ .AddMeter(CommandServiceMetrics.MeterName)
+ .AddMetrics(customTags)
+ .AddMeter(PersistenceMetrics.MeterName)
+ .AddMetrics(customTags);
- static MeterProviderBuilder AddMetrics<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this MeterProviderBuilder builder, TagList? customTags = null)
- where T : class, IWithCustomTags {
- builder.ConfigureServices(services => services.AddSingleton());
+ MeterProviderBuilder AddMetrics<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(TagList? customTags = null)
+ where T : class, IWithCustomTags {
+ builder.ConfigureServices(services => services.AddSingleton());
- return builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder
- ? deferredMeterProviderBuilder.Configure(
- (sp, b) => {
- b.AddInstrumentation(
- () => {
- var instrument = sp.GetRequiredService();
- if (customTags != null) instrument.SetCustomTags(customTags.Value);
+ return builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder
+ ? deferredMeterProviderBuilder.Configure(
+ (sp, b) => {
+ b.AddInstrumentation(
+ () => {
+ var instrument = sp.GetRequiredService();
+ if (customTags != null) instrument.SetCustomTags(customTags.Value);
- return instrument;
- }
- );
- }
- )
- : builder;
+ return instrument;
+ }
+ );
+ }
+ )
+ : builder;
+ }
}
}
diff --git a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/TracerProviderBuilderExtensions.cs b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/TracerProviderBuilderExtensions.cs
index 46028d001..4afe7f822 100644
--- a/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/TracerProviderBuilderExtensions.cs
+++ b/src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/TracerProviderBuilderExtensions.cs
@@ -25,7 +25,7 @@ class PollingSampler : Sampler {
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) {
return samplingParameters.ParentContext is { TraceFlags: ActivityTraceFlags.None } && samplingParameters is { Kind: ActivityKind.Client, Name: "eventuous" }
? new SamplingResult(SamplingDecision.Drop)
- : new SamplingResult(SamplingDecision.RecordAndSample);
+ : new(SamplingDecision.RecordAndSample);
}
}
}
diff --git a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
index bd966a785..000af0690 100644
--- a/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
+++ b/src/Diagnostics/test/Eventuous.Tests.OpenTelemetry/MetricsTests.cs
@@ -63,13 +63,15 @@ public void Teardown() {
}
static class TagExtensions {
- public static async Task CheckTag(this MetricValue metric, string tag, string expectedValue) {
- await Assert.That(metric.GetTag(tag)).IsEqualTo(expectedValue);
- }
+ extension(MetricValue metric) {
+ public async Task CheckTag(string tag, string expectedValue) {
+ await Assert.That(metric.GetTag(tag)).IsEqualTo(expectedValue);
+ }
- static object GetTag(this MetricValue metric, string key) {
- var index = metric.Keys.Select((x, i) => (x, i)).First(x => x.x == key).i;
+ object GetTag(string key) {
+ var index = metric.Keys.Select((x, i) => (x, i)).First(x => x.x == key).i;
- return metric.Values[index];
+ return metric.Values[index];
+ }
}
}
diff --git a/src/Experimental/src/ElasticPlayground/ConfigureElastic.cs b/src/Experimental/src/ElasticPlayground/ConfigureElastic.cs
index 2441fb2e1..499db9f27 100644
--- a/src/Experimental/src/ElasticPlayground/ConfigureElastic.cs
+++ b/src/Experimental/src/ElasticPlayground/ConfigureElastic.cs
@@ -8,25 +8,25 @@ public static class ConfigureElastic {
public static async Task ConfigureIndex(this ElasticClient client) {
var config = new IndexConfig {
IndexName = "eventuous",
- Lifecycle = new LifecycleConfig {
+ Lifecycle = new() {
PolicyName = "eventuous",
Tiers = [
- new TierDefinition {
+ new() {
Tier = "hot",
MinAge = "1d",
Priority = 100,
- Rollover = new Rollover {
+ Rollover = new() {
MaxAge = "1d",
MaxSize = "100mb"
}
},
- new TierDefinition {
+ new() {
Tier = "warm",
MinAge = "1d",
Priority = 50,
- ForceMerge = new ForceMerge { MaxNumSegments = 1 }
+ ForceMerge = new() { MaxNumSegments = 1 }
},
- new TierDefinition {
+ new() {
Tier = "cold",
MinAge = "1d",
Priority = 0,
@@ -34,9 +34,7 @@ public static async Task ConfigureIndex(this ElasticClient client) {
}
]
},
- Template = new DataStreamTemplateConfig {
- TemplateName = "eventuous"
- }
+ Template = new() { TemplateName = "eventuous" }
};
await client.CreateIndexIfNecessary(config);
diff --git a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj
index 865d23d52..3bf2878d0 100644
--- a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj
+++ b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
Exe
false
true
diff --git a/src/Experimental/src/ElasticPlayground/MiscExtensions.cs b/src/Experimental/src/ElasticPlayground/MiscExtensions.cs
index 309e01545..8f5837c79 100644
--- a/src/Experimental/src/ElasticPlayground/MiscExtensions.cs
+++ b/src/Experimental/src/ElasticPlayground/MiscExtensions.cs
@@ -2,16 +2,15 @@
// Licensed under the Apache License, Version 2.0.
using Eventuous.Sut.App;
-using Eventuous.Sut.Domain;
namespace ElasticPlayground;
public static class MiscExtensions {
public static Commands.RecordPayment ToRecordPayment(this Commands.BookRoom command, string paymentId, float divider = 1)
=> new(
- new BookingId(command.BookingId),
+ new(command.BookingId),
paymentId,
- new Money(command.Price / divider),
+ new(command.Price / divider),
DateTimeOffset.Now
);
}
diff --git a/src/Experimental/src/Eventuous.Spyglass/Accessor.cs b/src/Experimental/src/Eventuous.Spyglass/Accessor.cs
index 83115b119..c9906bdda 100644
--- a/src/Experimental/src/Eventuous.Spyglass/Accessor.cs
+++ b/src/Experimental/src/Eventuous.Spyglass/Accessor.cs
@@ -6,10 +6,12 @@
namespace Eventuous.Spyglass;
static class Accessor {
- public static object? GetPrivateMember(this object instance, string name) => GetMember(instance.GetType(), instance, name);
+ extension(object instance) {
+ public object? GetPrivateMember(string name) => GetMember(instance.GetType(), instance, name);
- public static TMember? GetPrivateMember(this object instance, string name) where TMember : class
- => GetMember(instance.GetType(), instance, name);
+ public TMember? GetPrivateMember(string name) where TMember : class
+ => GetMember(instance.GetType(), instance, name);
+ }
static TMember? GetMember(Type instanceType, object instance, string name) where TMember : class
=> GetMember(instanceType, instance, name) as TMember;
diff --git a/src/Experimental/src/Eventuous.Spyglass/InsidePeek.cs b/src/Experimental/src/Eventuous.Spyglass/InsidePeek.cs
index 516bf8c8b..e71f21728 100644
--- a/src/Experimental/src/Eventuous.Spyglass/InsidePeek.cs
+++ b/src/Experimental/src/Eventuous.Spyglass/InsidePeek.cs
@@ -48,7 +48,7 @@ void Scan(Assembly assembly) {
var methods = (type as dynamic).DeclaredMethods as MethodInfo[];
- AggregateInfos.Add(new AggregateInfo(type, stateType, methods!, () => CreateInstance(reg, type)));
+ AggregateInfos.Add(new(type, stateType, methods!, () => CreateInstance(reg, type)));
}
return;
@@ -70,7 +70,7 @@ static dynamic CreateInstance(Dictionary> reg, Type aggregat
public async Task
-
-
+
+
+
+
+
diff --git a/src/Extensions/gen/Eventuous.Extensions.AspNetCore.Generators/HttpCommandStateMismatchAnalyzer.cs b/src/Extensions/gen/Eventuous.Extensions.AspNetCore.Generators/HttpCommandStateMismatchAnalyzer.cs
index cae9c98c1..2a9280d2b 100644
--- a/src/Extensions/gen/Eventuous.Extensions.AspNetCore.Generators/HttpCommandStateMismatchAnalyzer.cs
+++ b/src/Extensions/gen/Eventuous.Extensions.AspNetCore.Generators/HttpCommandStateMismatchAnalyzer.cs
@@ -14,17 +14,6 @@ namespace Eventuous.Extensions.AspNetCore.Generators;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class HttpCommandStateMismatchAnalyzer : DiagnosticAnalyzer {
- public const string DiagnosticId = "EVTA001";
- public const string RouteDiagnosticId = "EVTA002";
-
- static readonly LocalizableString RouteMessageFormat =
- "Command {0} attribute route '{1}' does not match route override '{2}'";
-
- static readonly LocalizableString Description =
- "When using MapCommands().MapCommand(...), the TContract decorated with HttpCommandAttribute must have T matching the TState of the route builder.";
-
- static readonly LocalizableString RouteDescription =
- "When an HttpCommandAttribute specifies a Route and MapCommand is called with an explicit route override, the values should match.";
const string NamespaceName = "Eventuous.Extensions.AspNetCore.Http";
const string BuilderTypeName = "CommandServiceRouteBuilder";
@@ -33,7 +22,6 @@ public class HttpCommandStateMismatchAnalyzer : DiagnosticAnalyzer {
const string StateTypeParamName = "StateType";
const string RouteParamName = "Route";
-
public override ImmutableArray SupportedDiagnostics => [StateMatchRule, RouteRule];
public override void Initialize(AnalysisContext context) {
@@ -46,9 +34,8 @@ static void AnalyzeInvocation(SyntaxNodeAnalysisContext context) {
var invocation = (InvocationExpressionSyntax)context.Node;
// Get the invoked method symbol
- var symbol = context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol as IMethodSymbol;
- if (symbol == null) return;
+ if (context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol is not IMethodSymbol symbol) return;
// We care about MapCommand invocations only
if (symbol.Name != "MapCommand") return;
@@ -83,13 +70,11 @@ static void AnalyzeInvocation(SyntaxNodeAnalysisContext context) {
}
}
}
- else if (symbol is { ContainingType: not null } containing && containing.ContainingType is { } || symbol.ContainingType is { }) {
+ else if (symbol is { ContainingType: not null } || symbol.ContainingType is not null) {
// Fallback to previous logic using method symbol's containing type (in case receiver type retrieval fails)
var containingType = symbol.ContainingType;
- if (containingType != null
- && containingType.Name == BuilderTypeName
- && containingType.Arity == 1
+ if (containingType is { Name: BuilderTypeName, Arity: 1 }
&& containingType.ContainingNamespace.ToDisplayString() == NamespaceName
&& invocation.Expression is MemberAccessExpressionSyntax { Name: GenericNameSyntax { TypeArgumentList.Arguments.Count: 2 } gname }) {
var tState = containingType.TypeArguments.FirstOrDefault();
diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/CommandMappingRegistry.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/CommandMappingRegistry.cs
index 96111025a..df54900ae 100644
--- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/CommandMappingRegistry.cs
+++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/CommandMappingRegistry.cs
@@ -17,7 +17,7 @@ public static class CommandMappingRegistry {
public readonly record struct Bound(Type CommandType, ConfigureEndpoint Map);
static readonly ConcurrentDictionary> PerState = new();
- static readonly List All = [];
+ static readonly List All = [];
// TODO: Figure out what to do with it
// ReSharper disable once CollectionNeverQueried.Local
diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMapping.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMapping.cs
index 7edef5189..493ecad06 100644
--- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMapping.cs
+++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMapping.cs
@@ -17,110 +17,103 @@ namespace Microsoft.AspNetCore.Routing;
public delegate TCommand EnrichCommandFromHttpContext(TCommand command, HttpContext httpContext);
public static partial class RouteBuilderExtensions {
- ///
- /// Map command to HTTP POST endpoint.
- /// The HTTP command type should be annotated with attribute.
- ///
/// Endpoint route builder instance
- /// A function to populate command props from HttpContext
- /// Command type
- /// State type on which the command will operate
- ///
- public static RouteHandlerBuilder MapCommand(
- this IEndpointRouteBuilder builder,
- EnrichCommandFromHttpContext? enrichCommand = null
- )
- where TState : State, new()
- where TCommand : class {
- var attr = typeof(TCommand).GetAttribute();
-
- return builder.MapCommand(attr?.Route, enrichCommand, attr?.PolicyName);
- }
+ extension(IEndpointRouteBuilder builder) {
+ ///
+ /// Map command to HTTP POST endpoint.
+ /// The HTTP command type should be annotated with attribute.
+ ///
+ /// A function to populate command props from HttpContext
+ /// Command type
+ /// State type on which the command will operate
+ ///
+ public RouteHandlerBuilder MapCommand(
+ EnrichCommandFromHttpContext? enrichCommand = null
+ )
+ where TState : State, new()
+ where TCommand : class {
+ var attr = typeof(TCommand).GetAttribute();
- ///
- /// Map command to HTTP POST endpoint.
- ///
- /// Endpoint route builder instance
- /// HTTP API route
- /// A function to populate command props from HttpContext
- /// Authorization policy
- /// Command type
- /// State type on which the command will operate
- ///
- public static RouteHandlerBuilder MapCommand(
- this IEndpointRouteBuilder builder,
- [StringSyntax("Route")] string? route,
- EnrichCommandFromHttpContext? enrichCommand = null,
- string? policyName = null
- )
- where TState : State, new()
- where TCommand : class
- => MapInternal(
- builder,
- route,
- enrichCommand != null ? (command, context) => enrichCommand(command, context) : null,
- policyName
- );
-
- ///
- /// Creates an instance of for a given aggregate type, so you
- /// can explicitly map commands to HTTP endpoints.
- ///
- /// Endpoint route builder instance
- /// State type
- ///
- public static CommandServiceRouteBuilder MapCommands(this IEndpointRouteBuilder builder)
- where TState : State, new() => new(builder);
-
- ///
- /// Maps all commands annotated by to HTTP endpoints to be handled
- /// by where TState is the state type provided.
- /// Only use it if your application only handles commands for one state type.
- ///
- /// Endpoint route builder instance
- /// Exclude command types
- /// State type
- ///
- ///
- public static IEndpointRouteBuilder MapDiscoveredCommands(this IEndpointRouteBuilder builder, params Type[] exclude)
- where TState : State {
- foreach (var (commandType, map) in CommandMappingRegistry.GetForState(typeof(TState))) {
- if (exclude.Contains(commandType)) continue;
- map(builder);
+ return builder.MapCommand(attr?.Route, enrichCommand, attr?.PolicyName);
}
- // Bind commands that didn't have explicit state at generation time
- // AZ: Ignore for now, as it's not clear how to handle this case
- // foreach (var unbound in CommandMappingRegistry.GetWithoutState()) {
- // builder.LocalMap(typeof(TState), unbound.CommandType, unbound.Route, unbound.Policy);
- // }
- return builder;
- }
- ///
- /// Maps commands that are annotated either with and/or
- /// in given assemblies. Will use assemblies of the current
- /// application domain if no assembly is specified explicitly.
- ///
- /// Endpoint router builder instance
- /// Exclude command types
- ///
- ///
- [PublicAPI]
- public static IEndpointRouteBuilder MapDiscoveredCommands(this IEndpointRouteBuilder builder, params Type[] exclude) {
- // Use generated registry, no reflection/assembly scanning
- foreach (var (commandType, map) in CommandMappingRegistry.GetAll()) {
- if (exclude.Contains(commandType)) continue;
- map(builder);
+ ///
+ /// Map command to HTTP POST endpoint.
+ ///
+ /// HTTP API route
+ /// A function to populate command props from HttpContext
+ /// Authorization policy
+ /// Command type
+ /// State type on which the command will operate
+ ///
+ public RouteHandlerBuilder MapCommand(
+ [StringSyntax("Route")] string? route,
+ EnrichCommandFromHttpContext? enrichCommand = null,
+ string? policyName = null
+ )
+ where TState : State, new()
+ where TCommand : class
+ => MapInternal(
+ builder,
+ route,
+ enrichCommand != null ? (command, context) => enrichCommand(command, context) : null,
+ policyName
+ );
+
+ ///
+ /// Creates an instance of for a given aggregate type, so you
+ /// can explicitly map commands to HTTP endpoints.
+ ///
+ /// State type
+ ///
+ public CommandServiceRouteBuilder MapCommands()
+ where TState : State, new() => new(builder);
+
+ ///
+ /// Maps all commands annotated by to HTTP endpoints to be handled
+ /// by where TState is the state type provided.
+ /// Only use it if your application only handles commands for one state type.
+ ///
+ /// Exclude command types
+ /// State type
+ ///
+ ///
+ public IEndpointRouteBuilder MapDiscoveredCommands(params Type[] exclude)
+ where TState : State {
+ foreach (var (commandType, map) in CommandMappingRegistry.GetForState(typeof(TState))) {
+ if (exclude.Contains(commandType)) continue;
+
+ map(builder);
+ }
+
+ // Bind commands that didn't have explicit state at generation time
+ // AZ: Ignore for now, as it's not clear how to handle this case
+ // foreach (var unbound in CommandMappingRegistry.GetWithoutState()) {
+ // builder.LocalMap(typeof(TState), unbound.CommandType, unbound.Route, unbound.Policy);
+ // }
+ return builder;
}
- return builder;
- }
- // static void LocalMap(this IEndpointRouteBuilder builder, Type stateType, Type type, string? route, string? policyName) {
- // var genericMethod = MapMethod.MakeGenericMethod(stateType, type, type);
- // genericMethod.Invoke(null, [builder, route, null, policyName]);
- // }
+ ///
+ /// Maps commands that are annotated either with and/or
+ /// in given assemblies. Will use assemblies of the current
+ /// application domain if no assembly is specified explicitly.
+ ///
+ /// Exclude command types
+ ///
+ ///
+ [PublicAPI]
+ public IEndpointRouteBuilder MapDiscoveredCommands(params Type[] exclude) {
+ // Use generated registry, no reflection/assembly scanning
+ foreach (var (commandType, map) in CommandMappingRegistry.GetAll()) {
+ if (exclude.Contains(commandType)) continue;
+
+ map(builder);
+ }
- // static readonly MethodInfo MapMethod = typeof(RouteBuilderExtensions).GetMethod(nameof(MapInternal), BindingFlags.Static | BindingFlags.NonPublic)!;
+ return builder;
+ }
+ }
static RouteHandlerBuilder MapInternal(
IEndpointRouteBuilder builder,
diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMappingExt.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMappingExt.cs
index 7c1514ff6..b05d65980 100644
--- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMappingExt.cs
+++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/HttpCommandMappingExt.cs
@@ -13,41 +13,40 @@ namespace Microsoft.AspNetCore.Routing;
public delegate TCommand ConvertAndEnrichCommand(TContract command, HttpContext httpContext);
public static partial class RouteBuilderExtensions {
- ///
- /// Map command to HTTP POST endpoint.
- /// The HTTP command type should be annotated with attribute.
- ///
/// Endpoint route builder instance
- /// Function to convert HTTP command to domain command
- /// HTTP command type
- /// Domain command type
- /// State type
- ///
- public static RouteHandlerBuilder MapCommand(
- this IEndpointRouteBuilder builder,
- ConvertAndEnrichCommand convert
- ) where TState : State, new() where TCommand : class where TContract : class {
- var attr = typeof(TContract).GetAttribute();
+ extension(IEndpointRouteBuilder builder) {
+ ///
+ /// Map command to HTTP POST endpoint.
+ /// The HTTP command type should be annotated with attribute.
+ ///
+ /// Function to convert HTTP command to domain command
+ /// HTTP command type
+ /// Domain command type
+ /// State type
+ ///
+ public RouteHandlerBuilder MapCommand(
+ ConvertAndEnrichCommand convert
+ ) where TState : State, new() where TCommand : class where TContract : class {
+ var attr = typeof(TContract).GetAttribute();
- return MapInternal(builder, attr?.Route, convert, attr?.PolicyName);
- }
+ return MapInternal(builder, attr?.Route, convert, attr?.PolicyName);
+ }
- ///
- /// Map command to HTTP POST endpoint
- ///
- /// Endpoint route builder instance
- /// API route for the POST endpoint
- /// Function to convert HTTP command to domain command
- /// Optional authorization policy name
- /// HTTP command type
- /// Domain command type
- /// State type
- ///
- public static RouteHandlerBuilder MapCommand(
- this IEndpointRouteBuilder builder,
- [StringSyntax("Route")] string? route,
- ConvertAndEnrichCommand convert,
- string? policyName = null
- ) where TState : State, new() where TCommand : class where TContract : class
- => MapInternal(builder, route, convert, policyName);
+ ///
+ /// Map command to HTTP POST endpoint
+ ///
+ /// API route for the POST endpoint
+ /// Function to convert HTTP command to domain command
+ /// Optional authorization policy name
+ /// HTTP command type
+ /// Domain command type
+ /// State type
+ ///
+ public RouteHandlerBuilder MapCommand(
+ [StringSyntax("Route")] string? route,
+ ConvertAndEnrichCommand convert,
+ string? policyName = null
+ ) where TState : State, new() where TCommand : class where TContract : class
+ => MapInternal(builder, route, convert, policyName);
+ }
}
diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/ResultExtensions.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/ResultExtensions.cs
index b5376471b..034acc152 100644
--- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/ResultExtensions.cs
+++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/ResultExtensions.cs
@@ -9,46 +9,48 @@
namespace Eventuous.Extensions.AspNetCore;
static class ResultExtensions {
- public static IResult AsResult(this Result result) where TState : State, new() {
- return result.Match(
- Results.Ok,
- error => error.Exception switch {
- OptimisticConcurrencyException => AsProblem(error, Status409Conflict),
- AggregateNotFoundException => AsProblem(error, Status404NotFound),
- DomainException => AsValidationProblem(error, Status400BadRequest),
- _ => AsProblem(error, Status500InternalServerError)
- }
- );
-
- static IResult AsProblem(Result.Error error, int statusCode)
- => Results.Problem(PopulateDetails(new ProblemDetails(), error, statusCode));
-
- static IResult AsValidationProblem(Result.Error error, int statusCode)
- => Results.Problem(PopulateDetails(new ValidationProblemDetails(error.AsErrors()), error, statusCode));
- }
-
- public static ActionResult AsActionResult(this Result result) where TState : State, new() {
- return result.Match(
- ok => new OkObjectResult(ok),
- error =>
- error.Exception switch {
+ extension(Result result) where TState : State, new() {
+ public IResult AsResult() {
+ return result.Match(
+ Results.Ok,
+ error => error.Exception switch {
OptimisticConcurrencyException => AsProblem(error, Status409Conflict),
AggregateNotFoundException => AsProblem(error, Status404NotFound),
DomainException => AsValidationProblem(error, Status400BadRequest),
_ => AsProblem(error, Status500InternalServerError)
}
- );
+ );
+
+ static IResult AsProblem(Result.Error error, int statusCode)
+ => Results.Problem(PopulateDetails(new ProblemDetails(), error, statusCode));
+
+ static IResult AsValidationProblem(Result.Error error, int statusCode)
+ => Results.Problem(PopulateDetails(new ValidationProblemDetails(error.AsErrors()), error, statusCode));
+ }
+
+ public ActionResult AsActionResult() {
+ return result.Match(
+ ok => new OkObjectResult(ok),
+ error =>
+ error.Exception switch {
+ OptimisticConcurrencyException => AsProblem(error, Status409Conflict),
+ AggregateNotFoundException => AsProblem(error, Status404NotFound),
+ DomainException => AsValidationProblem(error, Status400BadRequest),
+ _ => AsProblem(error, Status500InternalServerError)
+ }
+ );
- static ActionResult AsProblem(Result.Error error, int statusCode) => CreateResult(error, new ProblemDetails(), statusCode);
+ static ActionResult AsProblem(Result.Error error, int statusCode) => CreateResult(error, new ProblemDetails(), statusCode);
- static ActionResult AsValidationProblem(Result.Error error, int statusCode)
- => CreateResult(error, new ValidationProblemDetails(error.AsErrors()), statusCode);
+ static ActionResult AsValidationProblem(Result.Error error, int statusCode)
+ => CreateResult(error, new ValidationProblemDetails(error.AsErrors()), statusCode);
- static ActionResult CreateResult(Result.Error error, T details, int statusCode) where T : ProblemDetails
- => new ObjectResult(PopulateDetails(details, error, statusCode)) {
- StatusCode = statusCode,
- ContentTypes = [ContentTypes.ProblemDetails]
- };
+ static ActionResult CreateResult(Result.Error error, T details, int statusCode) where T : ProblemDetails
+ => new ObjectResult(PopulateDetails(details, error, statusCode)) {
+ StatusCode = statusCode,
+ ContentTypes = [ContentTypes.ProblemDetails]
+ };
+ }
}
static T PopulateDetails(T details, Result.Error error, int statusCode) where T : ProblemDetails where TState : State, new() {
diff --git a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/RouteHandlerBuilderExt.cs b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/RouteHandlerBuilderExt.cs
index b1e954b39..294d08c7e 100644
--- a/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/RouteHandlerBuilderExt.cs
+++ b/src/Extensions/src/Eventuous.Extensions.AspNetCore/Http/RouteHandlerBuilderExt.cs
@@ -7,19 +7,43 @@
namespace Eventuous.Extensions.AspNetCore.Http;
static class RouteHandlerBuilderExt {
- public static RouteHandlerBuilder ProducesValidationProblemDetails(this RouteHandlerBuilder builder, int statusCode)
- => builder.Produces(statusCode, ContentTypes.ProblemDetails);
-
- public static RouteHandlerBuilder ProducesProblemDetails(this RouteHandlerBuilder builder, int statusCode)
- => builder.Produces(statusCode, ContentTypes.ProblemDetails);
-
- static RouteHandlerBuilder ProducesOk(this RouteHandlerBuilder builder, Type resultType)
- => builder.Produces(StatusCodes.Status200OK, resultType, ContentTypes.Json);
-
- public static RouteHandlerBuilder ProducesOk(this RouteHandlerBuilder builder) where TState : class, new()
- => builder.ProducesOk(typeof(Result.Ok));
-
- static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, Type commandType) => builder.Accepts(commandType, false, ContentTypes.Json);
-
- public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder) => builder.Accepts(typeof(T));
+ extension(RouteHandlerBuilder builder) {
+ ///
+ /// Configures the route to produce a payload
+ /// with the specified HTTP status code and the application/problem+json content type.
+ ///
+ /// The HTTP status code to declare for validation problem responses.
+ /// The same instance for chaining.
+ public RouteHandlerBuilder ProducesValidationProblemDetails(int statusCode)
+ => builder.Produces(statusCode, ContentTypes.ProblemDetails);
+
+ ///
+ /// Configures the route to produce a payload
+ /// with the specified HTTP status code and the application/problem+json content type.
+ ///
+ /// The HTTP status code to declare for problem responses.
+ /// The same instance for chaining.
+ public RouteHandlerBuilder ProducesProblemDetails(int statusCode)
+ => builder.Produces(statusCode, ContentTypes.ProblemDetails);
+
+ RouteHandlerBuilder ProducesOk(Type resultType)
+ => builder.Produces(StatusCodes.Status200OK, resultType, ContentTypes.Json);
+
+ ///
+ /// Declares a successful 200 OK response for the route with a JSON body
+ /// containing for the specified state type.
+ ///
+ /// The state type wrapped by the successful result.
+ /// The same instance for chaining.
+ public RouteHandlerBuilder ProducesOk() where TState : class, new()
+ => builder.ProducesOk(typeof(Result.Ok));
+
+ RouteHandlerBuilder Accepts(Type commandType) => builder.Accepts(commandType, false, ContentTypes.Json);
+ ///
+ /// Declares that the route accepts a JSON request body of the specified type.
+ ///
+ /// The request/command type accepted by the route.
+ /// The same instance for chaining.
+ public RouteHandlerBuilder Accepts() => builder.Accepts(typeof(T));
+ }
}
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs
index e478cdf48..939a034f6 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateFactory.cs
@@ -8,39 +8,40 @@ namespace Microsoft.Extensions.DependencyInjection;
[PublicAPI]
public static class AggregateFactoryContainerExtensions {
- ///
- /// Add an aggregate factory to the container, allowing to resolve aggregate dependencies.
- /// Do not use this if your aggregate has no dependencies and has a parameterless constructor.
- /// Must be followed by "UseAggregateFactory" for IHost or IApplicationBuilder.
- ///
///
- /// Aggregate factory function, which can get dependencies from the container.
- /// Aggregate type
- /// Aggregate state type
- ///
- public static IServiceCollection AddAggregate(this IServiceCollection services, Func createInstance)
- where T : Aggregate where TState : State, new() {
- services.TryAddSingleton();
- services.AddSingleton(new ResolveAggregateFactory(typeof(T), createInstance));
+ extension(IServiceCollection services) {
+ ///
+ /// Add an aggregate factory to the container, allowing to resolve aggregate dependencies.
+ /// Do not use this if your aggregate has no dependencies and has a parameterless constructor.
+ /// Must be followed by "UseAggregateFactory" for IHost or IApplicationBuilder.
+ ///
+ /// Aggregate factory function, which can get dependencies from the container.
+ /// Aggregate type
+ /// Aggregate state type
+ ///
+ public IServiceCollection AddAggregate(Func createInstance)
+ where T : Aggregate where TState : State, new() {
+ services.TryAddSingleton();
+ services.AddSingleton(new ResolveAggregateFactory(typeof(T), createInstance));
- return services;
- }
+ return services;
+ }
- ///
- /// Add a default aggregate factory to the container, allowing to resolve aggregate dependencies.
- /// Do not use this if your aggregate has no dependencies and has a parameterless constructor.
- /// Must be followed by builder.UseAggregateFactory() in Startup.Configure.
- ///
- ///
- /// Aggregate type
- /// Aggregate state type
- ///
- public static IServiceCollection AddAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, TState>(this IServiceCollection services) where T : Aggregate where TState : State, new() {
- services.TryAddSingleton();
- services.AddTransient();
- // ReSharper disable once ConvertToLocalFunction
- Func createInstance = sp => sp.GetRequiredService();
+ ///
+ /// Add a default aggregate factory to the container, allowing to resolve aggregate dependencies.
+ /// Do not use this if your aggregate has no dependencies and has a parameterless constructor.
+ /// Must be followed by builder.UseAggregateFactory() in Startup.Configure.
+ ///
+ /// Aggregate type
+ /// Aggregate state type
+ ///
+ public IServiceCollection AddAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, TState>() where T : Aggregate where TState : State, new() {
+ services.TryAddSingleton();
+ services.AddTransient();
+ // ReSharper disable once ConvertToLocalFunction
+ Func createInstance = sp => sp.GetRequiredService();
- return services.AddSingleton(new ResolveAggregateFactory(typeof(T), createInstance));
+ return services.AddSingleton(new ResolveAggregateFactory(typeof(T), createInstance));
+ }
}
}
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateStore.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateStore.cs
index c2be8714b..d289c7da6 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateStore.cs
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/AggregateStore.cs
@@ -8,64 +8,65 @@
namespace Microsoft.Extensions.DependencyInjection;
public static class AggregateStoreRegistrationExtensions {
- ///
- /// Registers the aggregate store using the supplied type
- ///
///
- /// Implementation of
- ///
- [Obsolete("Use AddEventStore instead.")]
- public static IServiceCollection AddAggregateStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services)
- where T : class, IEventStore {
- services.TryAddSingleton();
- services.TryAddSingleton();
-
- if (EventuousDiagnostics.Enabled) { services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService())); }
- else { services.TryAddSingleton(sp => sp.GetRequiredService()); }
+ extension(IServiceCollection services) {
+ ///
+ /// Registers the aggregate store using the supplied type
+ ///
+ /// Implementation of
+ ///
+ [Obsolete("Use AddEventStore instead.")]
+ public IServiceCollection AddAggregateStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
+ where T : class, IEventStore {
+ services.TryAddSingleton();
+ services.TryAddSingleton();
- services.AddSingleton();
+ if (EventuousDiagnostics.Enabled) { services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService())); }
+ else { services.TryAddSingleton(sp => sp.GetRequiredService()); }
- return services;
- }
-
- ///
- /// Registers the aggregate store using the supplied type
- ///
- ///
- /// Function to create an instance of
- /// Implementation of
- ///
- [Obsolete("Use AddEventStore instead.")]
- public static IServiceCollection AddAggregateStore(this IServiceCollection services, Func getService) where T : class, IEventStore {
- services.TryAddSingleton();
+ services.AddSingleton();
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton(getService);
- services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService()));
+ return services;
}
- else { services.TryAddSingleton(getService); }
- services.AddSingleton();
+ ///
+ /// Registers the aggregate store using the supplied type
+ ///
+ /// Function to create an instance of
+ /// Implementation of
+ ///
+ [Obsolete("Use AddEventStore instead.")]
+ public IServiceCollection AddAggregateStore(Func getService) where T : class, IEventStore {
+ services.TryAddSingleton();
- return services;
- }
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton(getService);
+ services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(getService); }
- [Obsolete("Use AddEventStore and TieredEventReader instead.")]
- public static IServiceCollection AddAggregateStore
- <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TArchive>(this IServiceCollection services)
- where T : class, IEventStore where TArchive : class, IEventReader {
- services.TryAddSingleton();
+ services.AddSingleton();
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton();
- services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService()));
+ return services;
}
- else { services.TryAddSingleton(); }
- services.TryAddSingleton();
- services.AddSingleton>();
+ [Obsolete("Use AddEventStore and TieredEventReader instead.")]
+ public IServiceCollection AddAggregateStore
+ <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TArchive>()
+ where T : class, IEventStore where TArchive : class, IEventReader {
+ services.TryAddSingleton();
+
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => TracedEventStore.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(); }
- return services;
+ services.TryAddSingleton();
+ services.AddSingleton>();
+
+ return services;
+ }
}
}
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Services.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Services.cs
index 1db9419f3..09021ae0b 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Services.cs
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Services.cs
@@ -8,47 +8,48 @@
namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions {
- ///
- /// Registers the application service in the container
- ///
///
- ///
- ///
- ///
- public static IServiceCollection AddCommandService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, TState>(this IServiceCollection services)
- where T : class, ICommandService
- where TState : State, new() {
- services.AddSingleton();
-
- if (EventuousDiagnostics.Enabled) {
- services.AddSingleton(sp => TracedCommandService.Trace(sp.GetRequiredService()));
+ extension(IServiceCollection services) {
+ ///
+ /// Registers the application service in the container
+ ///
+ ///
+ ///
+ ///
+ public IServiceCollection AddCommandService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, TState>()
+ where T : class, ICommandService
+ where TState : State, new() {
+ services.AddSingleton();
+
+ if (EventuousDiagnostics.Enabled) {
+ services.AddSingleton(sp => TracedCommandService.Trace(sp.GetRequiredService()));
+ }
+ else {
+ services.AddSingleton>(sp => sp.GetRequiredService());
+ }
+
+ return services;
}
- else {
- services.AddSingleton>(sp => sp.GetRequiredService());
- }
-
- return services;
- }
- ///
- /// Registers the application service in the container
- ///
- ///
- /// Function to create an app service instance
- ///
- ///
- ///
- public static IServiceCollection AddCommandService(this IServiceCollection services, Func getService)
- where T : class, ICommandService where TState : State, new() {
- services.AddSingleton(getService);
-
- if (EventuousDiagnostics.Enabled) {
- services.AddSingleton(sp => TracedCommandService.Trace(sp.GetRequiredService()));
- }
- else {
- services.AddSingleton>(sp => sp.GetRequiredService());
+ ///
+ /// Registers the application service in the container
+ ///
+ /// Function to create an app service instance
+ ///
+ ///
+ ///
+ public IServiceCollection AddCommandService(Func getService)
+ where T : class, ICommandService where TState : State, new() {
+ services.AddSingleton(getService);
+
+ if (EventuousDiagnostics.Enabled) {
+ services.AddSingleton(sp => TracedCommandService.Trace(sp.GetRequiredService()));
+ }
+ else {
+ services.AddSingleton>(sp => sp.GetRequiredService());
+ }
+
+ return services;
}
-
- return services;
}
}
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/StoreWithArchive.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/StoreWithArchive.cs
index ba6bfc29a..9349c73b3 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/StoreWithArchive.cs
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/StoreWithArchive.cs
@@ -8,48 +8,46 @@
namespace Microsoft.Extensions.DependencyInjection;
public static class StoreWithArchiveRegistrations {
- ///
- /// Registers an event store service as reader, writer, and event store
- ///
///
- /// Implementation of that points to the hot store
- /// Implementation of that points to the archive
- ///
- public static IServiceCollection AddEventStore
+ extension(IServiceCollection services) {
+ ///
+ /// Registers an event store service as reader, writer, and event store
+ ///
+ /// Implementation of that points to the hot store
+ /// Implementation of that points to the archive
+ ///
+ public IServiceCollection AddEventStore
<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THotStore,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TArchiveStore>(
- this IServiceCollection services
- )
- where THotStore : class, IEventStore
- where TArchiveStore : class, IEventReader {
- services.TryAddSingleton();
- services.TryAddSingleton();
- services.AddSingleton(sp => new TieredEventStore(sp.GetRequiredService(), sp.GetRequiredService()));
- AddReaderWriter(services);
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TArchiveStore>()
+ where THotStore : class, IEventStore
+ where TArchiveStore : class, IEventReader {
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ services.AddSingleton(sp => new TieredEventStore(sp.GetRequiredService(), sp.GetRequiredService()));
+ AddReaderWriter(services);
- return services;
- }
+ return services;
+ }
- ///
- /// Registers an event store service as reader, writer, and event store
- ///
- ///
- /// Function to create an instance of that points to the hot store
- /// Function to create an instance of that points to the archive store
- /// Implementation of that points to the hot store
- /// Implementation of that points to the archive
- ///
- public static IServiceCollection AddEventStore(
- this IServiceCollection services,
- Func getHotStore,
- Func getArchive
- )
- where THotStore : class, IEventStore
- where TArchiveStore : class, IEventReader {
- services.AddSingleton(sp => new TieredEventStore(getHotStore(sp), getArchive(sp)));
- AddReaderWriter(services);
+ ///
+ /// Registers an event store service as reader, writer, and event store
+ ///
+ /// Function to create an instance of that points to the hot store
+ /// Function to create an instance of that points to the archive store
+ /// Implementation of that points to the hot store
+ /// Implementation of that points to the archive
+ ///
+ public IServiceCollection AddEventStore(
+ Func getHotStore,
+ Func getArchive
+ )
+ where THotStore : class, IEventStore
+ where TArchiveStore : class, IEventReader {
+ services.AddSingleton(sp => new TieredEventStore(getHotStore(sp), getArchive(sp)));
+ AddReaderWriter(services);
- return services;
+ return services;
+ }
}
static void AddReaderWriter(IServiceCollection services) {
diff --git a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Stores.cs b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Stores.cs
index da8d4801a..068d593bd 100644
--- a/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Stores.cs
+++ b/src/Extensions/src/Eventuous.Extensions.DependencyInjection/Registrations/Stores.cs
@@ -10,159 +10,154 @@ namespace Microsoft.Extensions.DependencyInjection;
[PublicAPI]
public static partial class ServiceCollectionExtensions {
- ///
- /// Registers the event reader
- ///
///
- /// Implementation of
- ///
- public static IServiceCollection AddEventReader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services) where T : class, IEventReader {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton();
- services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ extension(IServiceCollection services) {
+ ///
+ /// Registers the event reader
+ ///
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventReader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class, IEventReader {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(); }
+
+ return services;
}
- else { services.TryAddSingleton(); }
- return services;
- }
-
- ///
- /// Registers the event reader
- ///
- ///
- /// Function to create an instance of
- /// Implementation of
- ///
- public static IServiceCollection AddEventReader(this IServiceCollection services, Func getService) where T : class, IEventReader {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton(getService);
- services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ ///
+ /// Registers the event reader
+ ///
+ /// Function to create an instance of
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventReader(Func getService) where T : class, IEventReader {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton(getService);
+ services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(getService); }
+
+ return services;
}
- else { services.TryAddSingleton(getService); }
- return services;
- }
-
- ///
- /// Registers the event writer
- ///
- ///
- /// Implementation of
- ///
- public static IServiceCollection AddEventWriter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services) where T : class, IEventWriter {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton();
- services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ ///
+ /// Registers the event writer
+ ///
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventWriter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class, IEventWriter {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(); }
+
+ return services;
}
- else { services.TryAddSingleton(); }
- return services;
- }
-
- ///
- /// Registers the event writer
- ///
- ///
- /// Function to create an instance of
- /// Implementation of
- ///
- public static IServiceCollection AddEventWriter(this IServiceCollection services, Func getService) where T : class, IEventWriter {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton(getService);
- services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ ///
+ /// Registers the event writer
+ ///
+ /// Function to create an instance of
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventWriter(Func getService) where T : class, IEventWriter {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton(getService);
+ services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ }
+ else { services.TryAddSingleton(getService); }
+
+ return services;
}
- else { services.TryAddSingleton(getService); }
-
- return services;
- }
- ///
- /// Registers the event reader and writer
- ///
- ///
- /// Implementation of and
- ///
- public static IServiceCollection AddEventReaderWriter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services) where T : class, IEventWriter, IEventReader {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton();
- services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
- services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
- }
- else {
- services.TryAddSingleton();
- services.TryAddSingleton();
+ ///
+ /// Registers the event reader and writer
+ ///
+ /// Implementation of and
+ ///
+ public IServiceCollection AddEventReaderWriter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class, IEventWriter, IEventReader {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ }
+ else {
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ }
+
+ return services;
}
- return services;
- }
-
- ///
- /// Registers the event reader and writer implemented by one class
- ///
- ///
- /// Function to create an instance of the class,
- /// which implements both and
- /// Implementation of
- ///
- public static IServiceCollection AddEventReaderWriter(this IServiceCollection services, Func getService)
- where T : class, IEventWriter, IEventReader {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton(getService);
- services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
- services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
- }
- else {
- services.TryAddSingleton(getService);
- services.TryAddSingleton(getService);
+ ///
+ /// Registers the event reader and writer implemented by one class
+ ///
+ /// Function to create an instance of the class,
+ /// which implements both and
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventReaderWriter(Func getService)
+ where T : class, IEventWriter, IEventReader {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton(getService);
+ services.TryAddSingleton(sp => TracedEventReader.Trace(sp.GetRequiredService()));
+ services.TryAddSingleton(sp => TracedEventWriter.Trace(sp.GetRequiredService()));
+ }
+ else {
+ services.TryAddSingleton(getService);
+ services.TryAddSingleton(getService);
+ }
+
+ return services;
}
- return services;
- }
-
- ///
- /// Registers an event store service as reader, writer, and event store
- ///
- ///
- /// Implementation of
- ///
- public static IServiceCollection AddEventStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IServiceCollection services) where T : class, IEventStore {
- if (EventuousDiagnostics.Enabled) {
- services.TryAddSingleton();
- services.AddSingleton(sp => new TracedEventStore(sp.GetRequiredService()));
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
- services.AddSingleton(sp => sp.GetRequiredService());
+ ///
+ /// Registers an event store service as reader, writer, and event store
+ ///
+ /// Implementation of
+ ///
+ public IServiceCollection AddEventStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class, IEventStore {
+ if (EventuousDiagnostics.Enabled) {
+ services.TryAddSingleton();
+ services.AddSingleton(sp => new TracedEventStore(sp.GetRequiredService()));
+ services.AddSingleton(sp => sp.GetRequiredService