Skip to content

Aspire sample application#556

Open
quezlatch wants to merge 49 commits into
Eventuous:devfrom
quezlatch:aspire-only
Open

Aspire sample application#556
quezlatch wants to merge 49 commits into
Eventuous:devfrom
quezlatch:aspire-only

Conversation

@quezlatch

Copy link
Copy Markdown
Contributor

Stacked on #550

Aspire Sample

A new sample demonstrates how to use Eventuous in a distributed .NET Aspire application with Azure services.

  • Added new Aspire AppHost project (Bookings.AppHost) with distributed application configuration
  • Configured Azure Storage emulator with blob container support
  • Integrated Azure SQL Server, Service Bus, and Storage resources in the sample
  • Added Scalar.Aspire for API reference management between services
  • Updated both Bookings and Payments sample projects to reference Azure Storage Blobs package
  • Still uses Swashbuckle rather than MS OpenApi, as that clashes with JetBrains Annotations
  • Is pretty cool to see the debugger just work across distributed services
  • Requires Aspire CLI, and really an IDE extension

PR prepared with the help of Mistral Vibe.

quezlatch and others added 30 commits June 19, 2026 08:30
…StorageBlobsProjector

- Create new test project following Eventuous.Tests.Azure.ServiceBus structure
- Add Testcontainers.Azurite package to Directory.Packages.props
- Add IntegrationFixture with Azurite and KurrentDB containers
- Test all On method variants (sync/async, state/context) for new and existing blobs
- Test concurrent modification scenario (412 Precondition Failed)
- Test no handler scenario (returns Ignored)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…face intent

- Add helper methods: SetupContainer, SetupExistingBlob, GetBlobState, AssertSuccess, AssertIgnored
- Rename projector classes to surface handler patterns (SyncStateProjector, etc.)
- Group tests by handler variant with clear section comments
- Test names now follow [Variant]_[Scenario]_[ExpectedBehavior] pattern
- Reduce LOC from ~450 to ~330 (-27%)
- Remove fixture parameter from CreateContext (unused)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…obServiceClient and container name

- Add StorageBlobsProjector(BlobServiceClient, string containerName) constructor
- Update test helper methods to work with container names instead of BlobContainerClient
- Add GetContainer() helper to get BlobContainerClient from fixture
- Update all test projector classes with new constructor overload
- Update all tests to use fixture.BlobServiceClient with container names

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add Azure Aspire sample and Blob Storage projector with Azurite tests

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add .NET Aspire Azure Bookings sample orchestrating SQL, Service Bus, and Storage emulators.
• Introduce Azure Blob Storage projector for read models with retries and idempotency options.
• Add Azurite-based integration tests and update shared dependency versions/configuration.
Diagram

graph TD
  apphost["Aspire AppHost"] --> bookings["Bookings API"] --> sql[("Azure SQL")]
  apphost --> payments["Payments API"] --> sql
  payments --> sb{{"Service Bus"}} --> bookings
  bookings --> blob[["Blob Storage"]]
  apphost --> scalar["Scalar API Ref"] --> bookings --> sql
  scalar --> payments --> sql

  subgraph Legend
    direction LR
    _svc["Service"] ~~~ _db[("Database")] ~~~ _bus{{"Message bus"}} ~~~ _store[["Blob store"]]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use blob leases instead of ETag-based optimistic concurrency
  • ➕ Blob leases can provide stronger mutual exclusion semantics for hot blobs
  • ➕ May reduce retry loops under heavy contention
  • ➖ Adds lease acquisition/renewal complexity and more failure modes
  • ➖ ETags are simpler and usually sufficient for projection updates
2. Rely on subscription checkpointing only (omit projector idempotency)
  • ➕ Less metadata logic in blobs and simpler write path
  • ➕ Avoids potential false-positives if metadata is altered
  • ➖ Doesn’t protect against at-least-once delivery duplicates when replaying or reprocessing
  • ➖ Harder to reason about correctness when consumers can redeliver messages
3. Aspire-native OpenAPI aggregation (instead of Scalar.Aspire)
  • ➕ Potentially fewer third-party dependencies
  • ➕ Could align better with future Aspire dashboard features
  • ➖ May not offer the same API reference UX/feature set
  • ➖ PR notes indicate MS OpenApi conflicts in this repo’s dependency graph

Recommendation: The chosen approach is appropriate: ETag-based concurrency with optional retries is a good default for blob-backed projections, and the added idempotency modes are valuable for at-least-once delivery scenarios. Keep Scalar.Aspire for now given the stated OpenAPI package conflict; if that conflict is resolved later, reconsider swapping to Aspire-native tooling.

Files changed (41) +2077 / -7

Enhancement (26) +1015 / -1
AppHost.csDefine Aspire distributed app topology (SQL, Service Bus, Storage, Scalar) +41/-0

Define Aspire distributed app topology (SQL, Service Bus, Storage, Scalar)

• Creates the Aspire AppHost wiring Azure SQL (two DBs), Service Bus emulator + queue, and Storage emulator + blob container. Registers Bookings and Payments projects with references/health checks and adds Scalar API references.

samples/azure/Bookings.AppHost/AppHost.cs

Bookings.AppHost.csprojAdd Aspire AppHost project targeting net10.0 with Azure hosting packages +25/-0

Add Aspire AppHost project targeting net10.0 with Azure hosting packages

• Defines the AppHost executable using Aspire.AppHost.Sdk and references Bookings/Payments projects plus Aspire Azure hosting packages and Scalar.Aspire.

samples/azure/Bookings.AppHost/Bookings.AppHost.csproj

Bookings.Domain.csprojCreate Azure sample domain project reusing kurrentdb domain sources +16/-0

Create Azure sample domain project reusing kurrentdb domain sources

• Adds a domain project that references Eventuous core projects and links the existing Bookings.Domain source files from the kurrentdb sample.

samples/azure/Bookings.Domain/Bookings.Domain.csproj

Bookings.Payments.csprojCreate Payments service project with telemetry, Swagger, and Azure client dependencies +47/-0

Create Payments service project with telemetry, Swagger, and Azure client dependencies

• Defines the Payments web project, adds Azure.Storage.Blobs + Microsoft.Extensions.Azure and observability packages, links shared infrastructure sources, and references Eventuous SQL Server + ServiceBus components.

samples/azure/Bookings.Payments/Bookings.Payments.csproj

Payments.csAdd payments-to-integration gateway message transform +31/-0

Add payments-to-integration gateway message transform

• Implements a gateway transform that converts PaymentRecorded domain events into BookingPaymentRecorded integration events sent to the PaymentsIntegration stream/queue via Service Bus.

samples/azure/Bookings.Payments/Integration/Payments.cs

Program.csAdd Payments HTTP API host with Swagger and OpenTelemetry wiring +43/-0

Add Payments HTTP API host with Swagger and OpenTelemetry wiring

• Bootstraps the Payments service with controllers, Swagger (Swashbuckle), OpenTelemetry + Serilog, health checks, and Eventuous command mapping for RecordPayment.

samples/azure/Bookings.Payments/Program.cs

Registrations.csWire Payments Eventuous SQL Server store and Service Bus gateway via DI +37/-0

Wire Payments Eventuous SQL Server store and Service Bus gateway via DI

• Registers Azure clients for ServiceBus and BlobServiceClient, configures SQL Server event store + checkpoint store, and sets up an Eventuous Gateway that publishes to Service Bus.

samples/azure/Bookings.Payments/Registrations.cs

BookingsCommandService.csAdd Bookings command service handling BookRoom and RecordPayment +32/-0

Add Bookings command service handling BookRoom and RecordPayment

• Implements a CommandService mapping for new and existing booking command flows, including room booking with availability check and payment recording.

samples/azure/Bookings/Application/BookingsCommandService.cs

BookingsQueryService.csAdd query service for MyBookings projection access +7/-0

Add query service for MyBookings projection access

• Introduces a small query service that loads a user's MyBookings document from the keyed projections subscription.

samples/azure/Bookings/Application/BookingsQueryService.cs

Commands.csDefine booking command DTOs for the Azure sample +17/-0

Define booking command DTOs for the Azure sample

• Adds BookRoom and RecordPayment command records used by the HTTP APIs and command services.

samples/azure/Bookings/Application/Commands.cs

BookingDocument.csAdd blob-stored booking document model +16/-0

Add blob-stored booking document model

• Defines the BookingDocument shape persisted to Blob Storage for booking state queries.

samples/azure/Bookings/Application/Queries/BookingDocument.cs

BookingStateProjection.csAdd booking state projector to Azure Blob Storage +32/-0

Add booking state projector to Azure Blob Storage

• Implements a StorageBlobsProjector-based projection writing BookingDocument into the bookings-container, handling RoomBooked and payment-related events.

samples/azure/Bookings/Application/Queries/BookingStateProjection.cs

MyBookings.csAdd aggregated per-user bookings read model +10/-0

Add aggregated per-user bookings read model

• Defines MyBookings document containing an immutable list of a user’s bookings for query scenarios.

samples/azure/Bookings/Application/Queries/MyBookings.cs

MyBookingsProjection.csProject per-user bookings list into Blob Storage with event-reader fallback +38/-0

Project per-user bookings list into Blob Storage with event-reader fallback

• Implements a blob projection for MyBookings keyed by guestId, including a cancellation flow that loads state via IEventReader to resolve guestId when needed.

samples/azure/Bookings/Application/Queries/MyBookingsProjection.cs

Bookings.csprojCreate Bookings service project with Azure Blob projections and observability +36/-0

Create Bookings service project with Azure Blob projections and observability

• Defines the Bookings web project, adds Azure.Storage.Blobs + Microsoft.Extensions.Azure and telemetry packages, and references Eventuous SQL Server, ServiceBus, and Azure.Storage.Blobs components plus the domain project.

samples/azure/Bookings/Bookings.csproj

CommandApi.csAdd Bookings command controller endpoints +28/-0

Add Bookings command controller endpoints

• Adds HTTP endpoints for booking creation and (demo-only) direct RecordPayment command submission using Eventuous ASP.NET command base handlers.

samples/azure/Bookings/HttpApi/Bookings/CommandApi.cs

QueryApi.csAdd Bookings query controller for loading aggregate state +18/-0

Add Bookings query controller for loading aggregate state

• Adds a simple GET endpoint that loads BookingState from the event store via IEventReader.

samples/azure/Bookings/HttpApi/Bookings/QueryApi.cs

Logging.csAdd shared Serilog configuration for sample services +23/-0

Add shared Serilog configuration for sample services

• Defines Serilog setup with console output and OpenTelemetry sink, including overrides for common noisy sources.

samples/azure/Bookings/Infrastructure/Logging.cs

Telemetry.csAdd OpenTelemetry wiring gated by OTLP endpoint +33/-0

Add OpenTelemetry wiring gated by OTLP endpoint

• Adds metrics/tracing configuration (ASP.NET Core, HttpClient, SqlClient, Eventuous) and Prometheus/OTLP exporters, enabled only when OTEL_EXPORTER_OTLP_ENDPOINT is set.

samples/azure/Bookings/Infrastructure/Telemetry.cs

Payments.csAdd Service Bus integration handler to translate payments into booking commands +35/-0

Add Service Bus integration handler to translate payments into booking commands

• Implements an Eventuous subscription handler that consumes BookingPaymentRecorded integration events and issues RecordPayment commands to the Booking aggregate.

samples/azure/Bookings/Integration/Payments.cs

Program.csAdd Bookings HTTP host with Swagger, Spyglass, telemetry, and query route +55/-0

Add Bookings HTTP host with Swagger, Spyglass, telemetry, and query route

• Bootstraps the Bookings service with Swagger, Serilog + telemetry, controller routing, Eventuous Spyglass, and a minimal API endpoint for per-user bookings queries.

samples/azure/Bookings/Program.cs

Registrations.csWire Bookings Eventuous SQL Server store, projections, and Service Bus subscription +59/-0

Wire Bookings Eventuous SQL Server store, projections, and Service Bus subscription

• Registers Azure clients (ServiceBus + BlobServiceClient), configures SQL Server event store/checkpoint store, sets up blob projection subscription handlers, and adds Service Bus subscription for payment integration.

samples/azure/Bookings/Registrations.cs

ServiceBusSubscription.csMake eventSerializer parameter optional for ServiceBusSubscription constructor +1/-1

Make eventSerializer parameter optional for ServiceBusSubscription constructor

• Adjusts the constructor signature to provide a default null for IEventSerializer, improving call-site ergonomics and preserving existing behavior.

src/Azure/src/Eventuous.Azure.ServiceBus/Subscriptions/ServiceBusSubscription.cs

BlobStorageProjector.csAdd BlobStorageProjector for projecting events into Azure Blob Storage documents +243/-0

Add BlobStorageProjector for projecting events into Azure Blob Storage documents

• Introduces a generic projector that loads/creates per-stream JSON state blobs, applies registered event handlers, and writes back using ETag-based optimistic concurrency. Adds retry-on-conflict and optional idempotency checks using blob metadata (message id/global position).

src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs

BlobStorageProjectorOptions.csAdd projector options for JSON settings, race retries, and idempotency mode +53/-0

Add projector options for JSON settings, race retries, and idempotency mode

• Defines BlobStorageProjectorOptions and IdempotencyMode to configure serialization behavior, conflict retries, and duplicate message detection strategy.

src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjectorOptions.cs

Eventuous.Azure.Storage.Blobs.csprojCreate Eventuous.Azure.Storage.Blobs library project and packaging metadata +39/-0

Create Eventuous.Azure.Storage.Blobs library project and packaging metadata

• Adds a new package project referencing Azure.Storage.Blobs and Eventuous.Subscriptions, includes README packing, and exposes internals to the new test project.

src/Azure/src/Eventuous.Azure.Storage.Blobs/Eventuous.Azure.Storage.Blobs.csproj

Tests (4) +764 / -0
BlobStorageProjectorTests.csAdd Azurite integration tests for BlobStorageProjector behaviors +682/-0

Add Azurite integration tests for BlobStorageProjector behaviors

• Adds comprehensive tests covering sync/async handlers, context-aware variants, custom blob IDs, optimistic concurrency failure/retry behavior, and idempotency modes.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/BlobStorageProjectorTests.cs

Eventuous.Tests.Azure.Storage.Blobs.csprojCreate Azure Storage Blobs test project with Azurite container dependency +19/-0

Create Azure Storage Blobs test project with Azurite container dependency

• Defines the test project referencing the new blobs library and adds Testcontainers.Azurite + required dependencies for integration tests.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/Eventuous.Tests.Azure.Storage.Blobs.csproj

IntegrationFixture.csAdd Azurite-backed integration fixture for blob tests +49/-0

Add Azurite-backed integration fixture for blob tests

• Introduces a TUnit async fixture that starts/stops an Azurite container and exposes a BlobServiceClient for tests.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/Fixtures/IntegrationFixture.cs

TestEvent.csAdd test event type registration for blob projector tests +14/-0

Add test event type registration for blob projector tests

• Defines a TestEvent with EventType annotation and ensures event types are registered in TypeMap for the test assembly.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/TestEvent.cs

Documentation (2) +207 / -0
README.mdDocument Azure Aspire sample architecture and usage +95/-0

Document Azure Aspire sample architecture and usage

• Adds detailed instructions for installing/running Aspire, describes the SQL/ServiceBus/Blob flow, and includes a mermaid diagram explaining the payment integration pipeline.

samples/azure/README.md

README.mdAdd usage documentation for blob projections and configuration options +112/-0

Add usage documentation for blob projections and configuration options

• Documents how to implement a projection, configure blob IDs/naming, and tune concurrency/idempotency behavior, including code examples and option tables.

src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md

Other (9) +91 / -6
Directory.Packages.propsAdd Aspire/Scalar/Azure Blob packages and refresh OpenTelemetry versions +20/-6

Add Aspire/Scalar/Azure Blob packages and refresh OpenTelemetry versions

• Introduces centralized versions for the Aspire SDK and OpenTelemetry instrumentation, adds Azure Storage Blobs + Microsoft.Extensions.Azure, and includes Scalar.Aspire and Serilog.Sinks.OpenTelemetry. Adds Testcontainers.Azurite for integration testing.

Directory.Packages.props

Eventuous.slnxRegister new Azure Storage Blobs projects and Azure Aspire sample projects +8/-0

Register new Azure Storage Blobs projects and Azure Aspire sample projects

• Adds the Eventuous.Azure.Storage.Blobs library and its test project to the solution, and wires in the new Azure-based Bookings sample (AppHost, Bookings, Payments, Domain).

Eventuous.slnx

nuget.configAdd NuGet source mapping configuration +12/-0

Add NuGet source mapping configuration

• Introduces a repository-level nuget.config that clears sources and maps all packages to nuget.org.

nuget.config

appsettings.Development.jsonAdd development logging defaults for AppHost +8/-0

Add development logging defaults for AppHost

• Adds basic logging level configuration for development.

samples/azure/Bookings.AppHost/appsettings.Development.json

appsettings.jsonAdd AppHost logging defaults including DCP noise reduction +9/-0

Add AppHost logging defaults including DCP noise reduction

• Configures logging levels and suppresses Aspire.Hosting.Dcp log verbosity.

samples/azure/Bookings.AppHost/appsettings.json

appsettings.jsonAdd Payments Aspire-provided connection string placeholders +14/-0

Add Payments Aspire-provided connection string placeholders

• Defines placeholder connection strings for SQL/Service Bus/Blobs to be injected by Aspire, plus logging defaults.

samples/azure/Bookings.Payments/appsettings.json

appsettings.jsonAdd Bookings Aspire-provided connection string placeholders +8/-0

Add Bookings Aspire-provided connection string placeholders

• Defines placeholder connection strings to be injected by Aspire at runtime.

samples/azure/Bookings/appsettings.json

Directory.Build.propsSet Azure sample target framework defaults to net10.0 +7/-0

Set Azure sample target framework defaults to net10.0

• Adds a sample-scoped Directory.Build.props to pin target framework settings for the azure sample projects.

samples/azure/Directory.Build.props

aspire.config.jsonAdd Aspire CLI configuration pointing to Bookings.AppHost +5/-0

Add Aspire CLI configuration pointing to Bookings.AppHost

• Configures Aspire tooling to use the Bookings.AppHost project as the application entrypoint.

samples/azure/aspire.config.json

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. LoadDocument missing .NoContext() ✓ Resolved 📘 Rule violation ☼ Reliability
Description
BlobStorageProjector.LoadDocument performs async I/O (DownloadContentAsync) without applying the
repository .NoContext() convention, which can lead to deadlocks in certain synchronization-context
environments. This violates the async I/O convention requirement.
Code

src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs[R129-134]

+    public async Task<T?> LoadDocument(string id) {
+        try {
+            var blobName = GetBlobName(id);
+            var blobClient = ContainerClient.GetBlobClient(blobName);
+            BlobDownloadResult blobContent = await blobClient.DownloadContentAsync();
+            return ToObjectFromJson(blobContent.Content);
Evidence
PR Compliance ID 1 requires awaited I/O operations to use .NoContext(). In LoadDocument, the new
code awaits blobClient.DownloadContentAsync() without .NoContext().

CLAUDE.md: All I/O APIs Must Be Asynchronous and Use NoContext() for ConfigureAwait(false): CLAUDE.md: All I/O APIs Must Be Asynchronous and Use NoContext() for ConfigureAwait(false): CLAUDE.md: All I/O APIs Must Be Asynchronous and Use NoContext() for ConfigureAwait(false): CLAUDE.md: All I/O APIs Must Be Asynchronous and Use NoContext() for ConfigureAwait(false)
src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs[129-134]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`BlobStorageProjector.LoadDocument` awaits an I/O call without `.NoContext()`, violating the repository async-await convention.
## Issue Context
Rule requires that awaited I/O calls use `.NoContext()` (ConfigureAwait(false) convention).
## Fix Focus Areas
- src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs[129-134]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. samples/azure targets net10 only 📘 Rule violation ⚙ Maintainability
Description
The new Azure sample overrides TargetFrameworks to only net10.0, deviating from the required
net8/net9/net10 multi-targeting matrix. This risks inconsistent build/analyzer behavior versus the
rest of the repository.
Code

samples/azure/Directory.Build.props[R3-6]

+  <PropertyGroup>
+    <TargetFramework>net10.0</TargetFramework>
+    <TargetFrameworks>net10.0</TargetFrameworks>
+  </PropertyGroup>
Evidence
PR Compliance ID 5 requires targeting net8.0, net9.0, and net10.0. The new
samples/azure/Directory.Build.props explicitly sets TargetFrameworks to only net10.0 (and
projects like Bookings.AppHost also target only net10.0).

CLAUDE.md: Projects Must Target net8.0, net9.0, and net10.0 With C# Preview and Nullable Enabled: CLAUDE.md: Projects Must Target net8.0, net9.0, and net10.0 With C# Preview and Nullable Enabled: CLAUDE.md: Projects Must Target net8.0, net9.0, and net10.0 With C# Preview and Nullable Enabled: CLAUDE.md: Projects Must Target net8.0, net9.0, and net10.0 With C# Preview and Nullable Enabled
samples/azure/Directory.Build.props[3-6]
samples/azure/Bookings.AppHost/Bookings.AppHost.csproj[1-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Azure sample introduces `TargetFrameworks` set to only `net10.0`, which violates the repository requirement to target `net8.0`, `net9.0`, and `net10.0`.
## Issue Context
A repo-wide build matrix is required for consistency.
## Fix Focus Areas
- samples/azure/Directory.Build.props[3-6]
- samples/azure/Bookings.AppHost/Bookings.AppHost.csproj[1-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Wrong projector base type ✓ Resolved 🐞 Bug ≡ Correctness
Description
The Azure sample projections inherit from StorageBlobsProjector, but the new Azure blobs package
introduced in this PR defines BlobStorageProjector; StorageBlobsProjector does not exist, so the
Azure sample won’t compile.
Code

samples/azure/Bookings/Application/Queries/BookingStateProjection.cs[R11-16]

+public class BookingStateProjection : StorageBlobsProjector<BookingDocument> {
+    public BookingStateProjection(
+        BlobServiceClient client,
+        IOptions<JsonOptions> serializerOptions
+    ) : base(client, "bookings-container", serializerOptions.Value.SerializerOptions) {
+        On<V1.RoomBooked>(HandleRoomBooked);
Evidence
The sample projections explicitly inherit from StorageBlobsProjector, but the added Azure blobs
package defines BlobStorageProjector; with no StorageBlobsProjector present, the sample cannot
compile.

samples/azure/Bookings/Application/Queries/BookingStateProjection.cs[1-21]
samples/azure/Bookings/Application/Queries/MyBookingsProjection.cs[1-24]
src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs[9-30]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Azure sample projections (`BookingStateProjection`, `MyBookingsProjection`) inherit from `StorageBlobsProjector<T>`, but the only projector type provided by the new `Eventuous.Azure.Storage.Blobs` package is `BlobStorageProjector<T>`. This causes a compile error for the new Azure sample.
### Issue Context
The PR adds a new projector implementation in `src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs`, but the sample code references a different (non-existent) type name.
### Fix Focus Areas
- samples/azure/Bookings/Application/Queries/BookingStateProjection.cs[11-16]
- samples/azure/Bookings/Application/Queries/MyBookingsProjection.cs[12-23]
- src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs[25-27]
### Suggested fix
Replace `StorageBlobsProjector<T>` with `BlobStorageProjector<T>` in both sample projection classes (or add a compatibility alias/type named `StorageBlobsProjector<T>` that derives from `BlobStorageProjector<T>` if that name is intended).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Aspire AppHost SDK pinned 📘 Rule violation ⚙ Maintainability
Description
Bookings.AppHost.csproj pins Aspire.AppHost.Sdk/13.4.6 directly in the project file even though
an AspireSdkVersion property was added for centralized versioning. This introduces per-project
version pinning instead of central management.
Code

samples/azure/Bookings.AppHost/Bookings.AppHost.csproj[1]

+<Project Sdk="Aspire.AppHost.Sdk/13.4.6">
Evidence
PR Compliance ID 7 requires avoiding per-project version pins in project files. This PR adds a
central AspireSdkVersion property in Directory.Packages.props, but the AppHost project still
hard-codes Aspire.AppHost.Sdk/13.4.6 in the csproj.

CLAUDE.md: Use Central Package Version Management via Directory.Packages.props: CLAUDE.md: Use Central Package Version Management via Directory.Packages.props: CLAUDE.md: Use Central Package Version Management via Directory.Packages.props: CLAUDE.md: Use Central Package Version Management via Directory.Packages.props
samples/azure/Bookings.AppHost/Bookings.AppHost.csproj[1-1]
Directory.Packages.props[20-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The AppHost project pins the Aspire SDK version directly in `Sdk="Aspire.AppHost.Sdk/13.4.6"` instead of using a centralized version value.
## Issue Context
A central `AspireSdkVersion` was added in `Directory.Packages.props`, but the project SDK reference does not use it.
## Fix Focus Areas
- samples/azure/Bookings.AppHost/Bookings.AppHost.csproj[1-1]
- Directory.Packages.props[20-32]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Otel package versions misaligned ✓ Resolved 🐞 Bug ☼ Reliability
Description
Directory.Packages.props upgrades OpenTelemetry instrumentation packages to 1.16.0 but still
centrally pins OpenTelemetry.Extensions.Hosting (and OpenTelemetry.Api) to 1.15.3, creating a
dependency alignment / restore-risk for projects that reference both.
Code

Directory.Packages.props[R120-127]

+    <PackageVersion Include="OpenTelemetry" Version="$(OtelInstrumentationVersion)" />
+    <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="$(OtelInstrumentationVersion)" />
+    <PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.16.0-beta.1" />
+    <PackageVersion Include="OpenTelemetry.Exporter.Zipkin" Version="$(OtelInstrumentationVersion)" />
+    <PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="$(OtelInstrumentationVersion)" />
+    <PackageVersion Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.16.0-beta.1" />
+    <PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="$(OtelInstrumentationVersion)" />
+    <PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="$(OtelInstrumentationVersion)" />
Evidence
The repo pins OpenTelemetry.Extensions.Hosting/OpenTelemetry.Api to 1.15.3 while simultaneously
pinning instrumentation packages to 1.16.0; Eventuous.Diagnostics.OpenTelemetry directly
references both, so it is directly exposed to the mismatch.

Directory.Packages.props[20-27]
Directory.Packages.props[54-56]
Directory.Packages.props[120-127]
src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj[9-12]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces `OtelInstrumentationVersion=1.16.0` and updates several OpenTelemetry packages (instrumentations/exporters) to use it, but `OpenTelemetry.Extensions.Hosting` and `OpenTelemetry.Api` remain pinned to `1.15.3`. Projects that reference both (e.g., `Eventuous.Diagnostics.OpenTelemetry`) may encounter NuGet downgrade/conflict warnings or inconsistent dependency graphs.
### Issue Context
Central package management applies versions repo-wide; this change affects non-sample projects too (notably `src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry`).
### Fix Focus Areas
- Directory.Packages.props[20-27]
- Directory.Packages.props[54-56]
- Directory.Packages.props[120-127]
- src/Diagnostics/src/Eventuous.Diagnostics.OpenTelemetry/Eventuous.Diagnostics.OpenTelemetry.csproj[9-12]
### Suggested fix
Update `OpenTelemetry.Extensions.Hosting` (and likely `OpenTelemetry.Api`) to a version compatible with the chosen `OtelInstrumentationVersion` (e.g., set them to `$(OtelInstrumentationVersion)` as well), or split into multiple properties if specific packages must remain on different versions (but ensure the resulting dependency graph is consistent).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Misleading db connection key 🐞 Bug ⚙ Maintainability
Description
The Azure sample appsettings.json files define ConnectionStrings:database, but the services
actually read bookings-db/payments-db, so standalone/manual runs will throw “Connection string
not found” despite a database-ish key being present.
Code

samples/azure/Bookings/appsettings.json[R2-6]

+  "ConnectionStrings": {
+    "database": "from aspire",
+    "sbemulators": "from aspire",
+    "blobs": "from aspire"
+  },
Evidence
The Bookings Azure service reads bookings-db from configuration, but its appsettings only defines
database (similarly, Payments reads payments-db but only defines database).

samples/azure/Bookings/appsettings.json[1-7]
samples/azure/Bookings/Registrations.cs[23-32]
samples/azure/Bookings.Payments/appsettings.json[1-14]
samples/azure/Bookings.Payments/Registrations.cs[13-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Azure sample configuration includes an unused/misleading `ConnectionStrings:database` key, while the code requires `ConnectionStrings:bookings-db` / `ConnectionStrings:payments-db`. This makes the configuration confusing and causes immediate startup exceptions when running services outside Aspire (or without Aspire-injected configuration).
### Issue Context
Aspire will inject the correct connection strings when running via the AppHost, but the checked-in appsettings currently suggests the services use `database`.
### Fix Focus Areas
- samples/azure/Bookings/appsettings.json[1-8]
- samples/azure/Bookings/Registrations.cs[23-32]
- samples/azure/Bookings.Payments/appsettings.json[1-14]
- samples/azure/Bookings.Payments/Registrations.cs[13-21]
### Suggested fix
Replace `ConnectionStrings:database` with `ConnectionStrings:bookings-db` in the Bookings service and `ConnectionStrings:payments-db` in the Payments service (keeping `sbemulators` and `blobs` as-is), or remove the unused key entirely and document that these values are provided by Aspire.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs
Comment thread samples/azure/Directory.Build.props
Comment thread samples/azure/Bookings.AppHost/Bookings.AppHost.csproj
Comment thread samples/azure/Bookings/Application/Queries/BookingStateProjection.cs Outdated
Comment thread Directory.Packages.props
Comment thread samples/azure/Bookings/appsettings.json
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Test Results

 46 files  + 24   46 suites  +24   13m 0s ⏱️ - 3m 11s
366 tests +  4  366 ✅ +  4  0 💤 ±0  0 ❌ ±0 
685 runs  +312  685 ✅ +312  0 💤 ±0  0 ❌ ±0 

Results for commit 129eace. ± Comparison against base commit 7827d2f.

This pull request removes 5 and adds 9 tests. Note that renamed tests count towards both.
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(07/15/2026 20:17:12 +00:00)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(07/15/2026 20:17:12)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(8098c0d1-405d-4841-8bc7-03d8b622994a)
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 4, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-15T20:17:12.1284219+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 8, Timestamp: 2026-07-15T20:17:12.1284219+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-15T20:17:12.1284219+00:00 })
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(07/16/2026 14:12:32 +00:00)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(07/16/2026 14:12:32)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(8d235740-ac3b-4bb1-900c-2f5663321a4f)
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 4, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:12.2887311+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 8, Timestamp: 2026-07-16T14:08:12.2887311+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:12.2887311+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 4, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:16.8249402+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 8, Timestamp: 2026-07-16T14:08:16.8249402+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:16.8249402+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 4, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:25.9374538+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 8, Timestamp: 2026-07-16T14:08:25.9374538+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-07-16T14:08:25.9374538+00:00 })

♻️ This comment has been updated with latest results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant