Project Setup for Migration¶
Verified by tests
ServiceCollectionExtensionsTests, ServiceCollectionExtensionsTests — library CI run #31657041675 (2026-08-13)
This guide covers the NuGet package changes and initial configuration needed when migrating from Marten/Wolverine to Whizbang.
Package Changes¶
Remove Marten/Wolverine Packages¶
Remove these packages from your .csproj files:
Remove Marten/Wolverine Packages
<!-- REMOVE THESE -->
<PackageReference Include="Marten" Version="x.x.x" />
<PackageReference Include="Marten.AspNetCore" Version="x.x.x" />
<PackageReference Include="Marten.Events.Projections" Version="x.x.x" />
<PackageReference Include="Wolverine" Version="x.x.x" />
<PackageReference Include="Wolverine.Marten" Version="x.x.x" />
<PackageReference Include="WolverineFx.RabbitMQ" Version="x.x.x" />
<PackageReference Include="WolverineFx.AzureServiceBus" Version="x.x.x" />
<PackageReference Include="WolverineFx.Kafka" Version="x.x.x" />
Add Whizbang Packages¶
Add the Whizbang packages from NuGet.org:
Add Whizbang Packages
<ItemGroup>
<!-- Core Whizbang -->
<PackageReference Include="Whizbang.Core" Version="x.x.x" />
<PackageReference Include="Whizbang.Generators" Version="x.x.x"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
<!-- Data Layer (choose based on your preference) -->
<!-- Option A: EF Core (recommended for complex queries) -->
<PackageReference Include="Whizbang.Data.EFCore.Postgres" Version="x.x.x" />
<!-- Option B: Dapper (recommended for performance) -->
<PackageReference Include="Whizbang.Data.Dapper.Postgres" Version="x.x.x" />
<!-- Transports (include both for environment switching) -->
<PackageReference Include="Whizbang.Transports.RabbitMQ" Version="x.x.x" />
<PackageReference Include="Whizbang.Transports.AzureServiceBus" Version="x.x.x" />
<!-- Testing (for test projects) -->
<PackageReference Include="Whizbang.Testing" Version="x.x.x" />
</ItemGroup>
Configuration Changes¶
Program.cs Migration¶
Before (Marten/Wolverine):
Program.cs Migration
var builder = WebApplication.CreateBuilder(args);
// Marten configuration
builder.Services.AddMarten(opts => {
opts.Connection(builder.Configuration.GetConnectionString("postgres")!);
opts.Events.TenancyStyle = TenancyStyle.Conjoined;
opts.Events.AppendMode = EventAppendMode.Quick;
})
.IntegrateWithWolverine()
.AddAsyncDaemon(DaemonMode.HotCold);
// Wolverine configuration
builder.Host.UseWolverine(opts => {
opts.UseRabbitMq(builder.Configuration.GetConnectionString("rabbitmq")!)
.UseConventionalRouting()
.UseDurableOutbox();
});
var app = builder.Build();
After (Whizbang):
Program.cs Migration (2)
var builder = WebApplication.CreateBuilder(args);
// Register your EF Core DbContext (marked with [WhizbangDbContext])
builder.Services.AddDbContext<OrderDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("postgres")!));
// Whizbang configuration — fluent builder selects the storage provider + driver
builder.Services
.AddWhizbang()
.WithEFCore<OrderDbContext>()
.WithDriver.Postgres;
// Generated registrations (produced by Whizbang.Generators)
builder.Services.AddReceptors();
builder.Services.AddWhizbangDispatcher();
// Transport configuration (environment-based switching)
var useRabbitMQ = builder.Configuration.GetValue<bool>("UseRabbitMQ");
if (useRabbitMQ) {
// Local development with Aspire
builder.Services.AddRabbitMQTransport(
builder.Configuration.GetConnectionString("rabbitmq")!,
options => {
options.DefaultQueueName = "whizbang-events";
});
builder.Services.AddRabbitMQHealthChecks();
} else {
// Production with Azure Service Bus
builder.Services.AddAzureServiceBusTransport(
builder.Configuration.GetConnectionString("servicebus")!,
options => {
options.DefaultSubscriptionName = "order-service";
});
builder.Services.AddAzureServiceBusHealthChecks();
}
var app = builder.Build();
AppSettings Configuration¶
appsettings.Development.json (for local Aspire development):
AppSettings Configuration
{
"UseRabbitMQ": true,
"ConnectionStrings": {
"postgres": "Host=localhost;Database=myapp;Username=postgres;Password=postgres",
"rabbitmq": "amqp://guest:guest@localhost:5672"
}
}
appsettings.Production.json (for Azure deployment):
AppSettings Configuration (2)
{
"UseRabbitMQ": false,
"ConnectionStrings": {
"postgres": "Host=myapp.postgres.database.azure.com;Database=myapp;...",
"servicebus": "Endpoint=sb://myapp.servicebus.windows.net/;..."
}
}
Database Schema¶
Initialize Whizbang Schema¶
Whizbang uses a different database schema than Marten. Initialize it on startup.
EF Core path — the source generator emits an EnsureWhizbangDatabaseInitializedAsync() extension on your [WhizbangDbContext]-marked context:
Initialize Whizbang Schema
var app = builder.Build();
// Initialize Whizbang schema (generated extension method —
// creates inbox/outbox/event-store tables + PostgreSQL functions)
using (var scope = app.Services.CreateScope()) {
var dbContext = scope.ServiceProvider.GetRequiredService<OrderDbContext>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
await dbContext.EnsureWhizbangDatabaseInitializedAsync(logger);
}
app.Run();
Dapper path — pass initializeSchema: true at registration and the schema is initialized by a hosted service on startup:
Initialize Whizbang Schema (Dapper)
Schema Comparison¶
| Marten Table | Whizbang Table | Notes |
|---|---|---|
mt_events |
wh_event_store |
Event storage (stream_id + version per row) |
mt_streams |
wh_active_streams |
Active stream ownership/lease metadata |
mt_doc_* |
wh_per_* |
Read model storage (one table per perspective) |
mt_event_progression |
wh_perspective_cursors |
Perspective (projection) progress |
wolverine_incoming_envelopes |
wh_inbox |
Inbox messages |
wolverine_outgoing_envelopes |
wh_outbox |
Outbox messages |
Namespace Changes¶
Update your using statements:
Namespace Changes
// Remove
using Marten;
using Marten.Events;
using Marten.Events.Projections;
using Wolverine;
using Wolverine.Attributes;
// Add
using Whizbang.Core;
using Whizbang.Core.Messaging;
using Whizbang.Core.Perspectives;
Dependency Injection Changes¶
Service Registration¶
Before:
Service Registration
// Marten session injection
public class OrderService {
private readonly IDocumentSession _session;
public OrderService(IDocumentSession session) {
_session = session;
}
}
After:
Service Registration - OrderService
// Whizbang direct injection
public class OrderService {
private readonly IEventStore _eventStore;
private readonly IDispatcher _dispatcher;
public OrderService(IEventStore eventStore, IDispatcher dispatcher) {
_eventStore = eventStore;
_dispatcher = dispatcher;
}
}
Verification Steps¶
After updating packages and configuration:
-
Build the solution:
-
Check for source generator output: Look for generated files in
obj/Debug/net10.0/generated/ -
Run the app once — schema initialization happens on startup (the generated
EnsureWhizbangDatabaseInitializedAsync()call, or the DapperinitializeSchema: truehosted service). Hash-based tracking makes re-runs cheap no-ops. -
Run tests:
Common Issues¶
Missing Receptors¶
If handlers aren't being discovered:
- Ensure Whizbang.Generators is referenced with OutputItemType="Analyzer"
- Verify classes implement IReceptor<TMessage> or IReceptor<TMessage, TResult>
- Check that receptor classes are public
Database Connection¶
If database operations fail: - Verify connection string format for Whizbang (same as Npgsql) - Ensure schema initialization ran successfully - Check that PostgreSQL extensions are installed (if using custom types)
Previous: Concept Mapping | Next: Handler Migration