diff --git a/docs/guide/codegen.md b/docs/guide/codegen.md
index 821641b0d..608fc1cb8 100644
--- a/docs/guide/codegen.md
+++ b/docs/guide/codegen.md
@@ -5,10 +5,6 @@ If you are experiencing noticeable startup lags or seeing spikes in memory utili
Wolverine, you will want to pursue using either the `Auto` or `Static` modes for code generation as explained in this guide.
:::
-::: tip
-This blog post from Oskar Dudycz will apply to Wolverine as well: [How to create a Docker image for the Marten application](https://event-driven.io/en/marten_and_docker/)
-:::
-
Wolverine uses runtime code generation to create the "adaptor" code that Wolverine uses to call into
your message handlers. Wolverine's [middleware strategy](/guide/handlers/middleware) also uses this strategy to "weave" calls to
middleware directly into the runtime pipeline without requiring the copious usage of adapter interfaces
@@ -25,7 +21,7 @@ Not to worry though, Wolverine has several facilities to either preview the gene
really understand how Wolverine is interacting with your code and to optimize the "cold start" by generating the dynamic
code ahead of time so that it can be embedded directly into your application's main assembly and discovered from there.
-By default, Wolverine runs with "dynamic" code generation where all the necessary generated types are built on demand
+By default, Wolverine runs with "dynamic" code generation where a l the necessary generated types are built on demand
the first time they are needed. This is perfect for a quick start to Wolverine, and might be fine in smaller projects even
at production time.
@@ -81,6 +77,8 @@ Most of the facilities shown here will require the [Oakton command line integrat
## Embedding Codegen in Docker
+This blog post from Oskar Dudycz will apply to Wolverine as well: [How to create a Docker image for the Marten application](https://event-driven.io/en/marten_and_docker/)
+
At this point, the most successful mechanism and sweet spot is to run the codegen as `Dynamic` at development time, but generating
the code artifacts just in time for production deployments. From Wolverine's sibling project Marten, see this section on [Application project setup](https://martendb.io/devops/devops.html#application-project-set-up)
for embedding the code generation directly into your Docker images for deployment.
@@ -124,6 +122,122 @@ using var host = Host.CreateDefaultBuilder()
If the assembly choice is correct, and the expected code files are really in `Internal/Generated` exactly as you'd expect, make
sure there's no accidental `` nodes in your project file. *Don't laugh, that's actually happened to Wolverine users*
+## Wolverine Code Generation and IoC
+
+::: info
+Why, you ask, does Wolverine do any of this? Wolverine was originally conceived of as the successor to the
+[FubuMVC & FubuTransportation](https://fubumvc.github.io) projects from the early 2010's. A major lesson learned
+from FubuMVC was that we needed to reduce object allocations, layering, runaway `Exception` stack traces, and allow
+for more flexible and streamlined handler or endpoint method signatures. To that end we fully embraced using runtime code
+generation -- and this was built well before source generators were available.
+
+As for the IoC part of this strategy, we ask you, what's the very fastest IoC tool in .NET? The answer of course, is
+"no IoC container."
+:::
+
+Wolverine's code generation uses the configuration of your IoC tool to create the generated code wrappers
+around your raw message handlers, HTTP endpoints, and middleware methods. Whenever possible, Wolverine is trying to
+completely eliminate your application's IoC tool from the runtime code by generating the necessary constructor function
+invocations to exactly mimic your application's IoC configuration.
+
+::: info
+Because you should care about this, Wolverine is absolutely generating `using` or `await using` for any objects it
+creates through constructor calls that implements `IDisposable` or `IAsyncDisposable`.
+:::
+
+When generating the adapter classes, Wolverine can infer which method arguments or type dependencies can be sourced
+from your application's IoC container configuration. If Wolverine can determine a way to generate all the necessary
+constructor calls to create any necessary services registered with a `Scoped` or `Transient` lifetime, Wolverine will generate
+code with the constructors. In this case, any IoC services that are registered with a `Singleton` lifetime
+will be "inlined" as constructor arguments into the generated adapter class itself for a little better efficiency.
+
+::: warning
+The usage of a service locator within the generated code will naturally be a little less efficient just because there
+is more runtime overhead. More dangerously, the service locator usage can sometimes foul up the scoping of services
+like Wolverine's `IMessageBus` or Marten's `IDocumentSession` that are normally built outside of the IoC container
+:::
+
+If Wolverine cannot determine a path to generate
+code for raw constructor construction of any registered services for a message handler or HTTP endpoint, Wolverine
+will fall back to generating code with the [service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern)
+using a scoped container (think [IServiceScopeFactory](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.iservicescopefactory?view=net-9.0-pp)).
+
+Here's some facts you do need to know about this whole process:
+
+* The adapter classes generated by Wolverine for both message handlers and HTTP endpoints are effectively singleton
+ scoped and only ever built once
+* Wolverine will try to bring `Singleton` scoped services through the generated adapter type's constructor function *one time*
+* Wolverine will have to fall back to the service locator usage if any service dependency that has a `Scoped` or `Transient`
+ lifetime is either an `internal` type or uses an "opaque" Lambda registration (think `IServiceCollection.AddScoped(s => {})`)
+
+::: tip
+The code generation using IoC configuration is tested with both the built in .NET `ServiceProvider` and [Lamar](https://jasperfx.github.io/lamar). It
+is theoretically possible to use other IoC tools with Wolverine, but only if you are *only* using `IServiceCollection`
+for your IoC configuration.
+:::
+
+As of Wolverine 5.0, you now have the ability to better control the usage of the service locator in Wolverine's
+code generation to potentially avoid unwanted usage:
+
+
+
+```cs
+var builder = Host.CreateApplicationBuilder();
+builder.UseWolverine(opts =>
+{
+ // This is the default behavior. Wolverine will allow you to utilize
+ // service location in the codegen, but will warn you through log messages
+ // when this happens
+ opts.ServiceLocationPolicy = ServiceLocationPolicy.AllowedButWarn;
+
+ // Tell Wolverine to just be quiet about service location and let it
+ // all go. For any of you with small children, I defy you to get the
+ // Frozen song out of your head now...
+ opts.ServiceLocationPolicy = ServiceLocationPolicy.AlwaysAllowed;
+
+ // Wolverine will throw exceptions at runtime if it encounters
+ // a message handler or HTTP endpoint that would require service
+ // location in the code generation
+ // Use this option to disallow any undesirably service location
+ opts.ServiceLocationPolicy = ServiceLocationPolicy.NotAllowed;
+});
+```
+snippet source | anchor
+
+
+::: note
+[Wolverine.HTTP has some additional control over the service locator](/guide/http/#using-the-httpcontext-requestservices) to utilize the shared scoped container
+with the rest of the AspNetCore pipeline.
+:::
+
+## Allow List for Service Location
+
+Wolverine always reverts to using a service locator when it encounters an "opaque" Lambda registration that has either
+a `Scoped` or `Transient` service lifetime. You can explicitly create an "allow" list of service types that can use
+a service locator pattern while allowing the rest of the code generation for the message handler or HTTP endpoint to use
+the more predictable and efficient generated constructor functions with this syntax:
+
+
+
+```cs
+var builder = Host.CreateApplicationBuilder();
+builder.UseWolverine(opts =>
+{
+ // other configuration
+
+ // Use a service locator for this service w/o forcing the entire
+ // message handler adapter to use a service locator for everything
+ opts.CodeGeneration.AlwaysUseServiceLocationFor();
+});
+```
+snippet source | anchor
+
+
+For example, this functionality might be helpful for:
+
+* [Refit proxies](https://github.com/reactiveui/refit) that are registered in IoC with a Lambda registration, but might not use any other services
+* EF Core `DbContext` types that might require some runtime configuration to construct themselves, but don't use other services (a [JasperFx Software](https://jasperfx.net) client
+ ran into this needing to conditionally opt into read replica usage, so hence, this feature made it into Wolverine 5.0)
## Environment Check for Expected Types
@@ -157,7 +271,8 @@ await host.StartAsync();
Do note that you would have to opt into using the environment checks on application startup, and maybe even force .NET
to make hosted service failures stop the application.
-See [Oakton's Environment Check functionality](https://jasperfx.github.io/oakton/guide/host/environment.html) for more information.
+See [Oakton's Environment Check functionality](https://jasperfx.github.io/oakton/guide/host/environment.html) for more information (the old Oakton documentation is still relevant for
+JasperFx).
## Previewing the Generated Code
@@ -185,11 +300,9 @@ directly under the root of your project folder.
## Optimized Workflow
-::: info
-Optimized Workflow overrides the storage migration [AutoBuildMessageStorageOnStartup](./durability/managing#disable-automatic-storage-migration) option, making it enabled for "Development" environment and disabled for other environments
-:::
-
-Or as a short hand option, use this:
+Wolverine and [Marten](https://martendb.io) both use the shared JasperFx library for their code generation,
+and you can configure different behavior for production versus development time for both tools (and any future
+"CritterStack" tools) with this usage:
diff --git a/docs/guide/durability/efcore/sagas.md b/docs/guide/durability/efcore/sagas.md
index 29b075ce8..e0e4af151 100644
--- a/docs/guide/durability/efcore/sagas.md
+++ b/docs/guide/durability/efcore/sagas.md
@@ -112,12 +112,9 @@ public class OrdersDbContext : DbContext
modelBuilder.Entity(map =>
{
map.ToTable("orders", "sample");
+ map.HasKey(x => x.Id);
map.Property(x => x.OrderStatus)
.HasConversion(v => v.ToString(), v => Enum.Parse(v));
-
- // enable optimistic concurrency
- map.Property(x => x.Version)
- .IsConcurrencyToken();
});
}
}
diff --git a/docs/guide/durability/marten/event-sourcing.md b/docs/guide/durability/marten/event-sourcing.md
index c408c8284..6eb00522c 100644
--- a/docs/guide/durability/marten/event-sourcing.md
+++ b/docs/guide/durability/marten/event-sourcing.md
@@ -6,6 +6,14 @@ need in a message handler or HTTP endpoint is a read-only copy of an event strea
instead that has a little bit lighter weight runtime within Marten.
:::
+::: info
+If your message handler or HTTP endpoint uses more than one declarative attribute for retrieving Marten data,
+Wolverine 5.0+ is able to utilize [Marten's Batch Querying capability](https://martendb.io/documents/querying/batched-queries.html#batched-queries) for more efficient interaction with the database.
+
+This batching behavior is also supported for all the declarative attributes and the "aggregate handler workflow" in general
+described in this page.
+:::
+
See the [OrderEventSourcingSample project on GitHub](https://github.com/JasperFx/wolverine/tree/main/src/Persistence/OrderEventSourcingSample) for more samples.
That Wolverine + Marten combination is optimized for efficient and productive development using a [CQRS architecture style](https://martinfowler.com/bliki/CQRS.html) with [Marten's event sourcing](https://martendb.io/events/) support.
diff --git a/docs/guide/durability/marten/index.md b/docs/guide/durability/marten/index.md
index e7616efde..9461a0f6d 100644
--- a/docs/guide/durability/marten/index.md
+++ b/docs/guide/durability/marten/index.md
@@ -104,6 +104,11 @@ Using the `IntegrateWithWolverine()` extension method behind your call to `AddMa
## Entity Attribute Loading
+::: info
+If your message handler or HTTP endpoint uses more than one declarative attribute for retrieving Marten data,
+Wolverine 5.0+ is able to utilize [Marten's Batch Querying capability](https://martendb.io/documents/querying/batched-queries.html#batched-queries) for more efficient interaction with the database.
+:::
+
The Marten integration is able to completely support the [Entity attribute usage](/guide/handlers/persistence.html#automatically-loading-entities-to-method-parameters).
## Marten as Outbox
diff --git a/docs/guide/http/index.md b/docs/guide/http/index.md
index 29cf21b8c..3b9b42690 100644
--- a/docs/guide/http/index.md
+++ b/docs/guide/http/index.md
@@ -204,5 +204,73 @@ return await app.RunJasperFxCommands(args);
snippet source | anchor
+## Using the HttpContext.RequestServices
+
+::: tip
+The opt in behavior to share the scoped services with the rest of the AspNetCore pipeline is useful
+for using Wolverine endpoints underneath AspNetCore middleware that "smuggles" state through the IoC container.
+
+Custom multi-tenancy middleware or custom authorization or other security middleware frequently does this. We think
+this will be helpful for mixed systems where Wolverine.HTTP is used for some routes while other routes are served
+by MVC Core or Minimal API or even some other kind of AspNetCore `Endpoint`.
+:::
+
+By default, any time [Wolverine has to revert to using a service locator](/guide/codegen.html#wolverine-code-generation-and-ioc)
+to generate the adapter code for an HTTP endpoint, Wolverine is using an isolated `IServiceScope` (or Lamar `INestedContainer`) within the generated code.
+
+But, with Wolverine 5.0+ you can opt into Wolverine just using the `HttpContext.RequestServices` so that you
+can share services with AspNetCore middleware. You can also configure *some* service types to be pulled from
+the `HttpContext.RequestServices` even if Wolverine is otherwise generating more efficient constructor calls
+for all other dependencies. Here's an example using both of these opt in behaviors:
+
+
+
+```cs
+var builder = WebApplication.CreateBuilder();
+
+builder.UseWolverine(opts =>
+{
+ // more configuration
+});
+
+// Just pretend that this IUserContext is being
+builder.Services.AddScoped();
+builder.Services.AddWolverineHttp();
+
+var app = builder.Build();
+
+// Custom middleware that is somehow configuring our IUserContext
+// that might be getting used within
+app.UseMiddleware();
+
+app.MapWolverineEndpoints(opts =>
+{
+ // Opt into using the shared HttpContext.RequestServices scoped
+ // container any time Wolverine has to use a service locator
+ opts.ServiceProviderSource = ServiceProviderSource.FromHttpContextRequestServices;
+
+ // OR this is the default behavior to be backwards compatible:
+ opts.ServiceProviderSource = ServiceProviderSource.IsolatedAndScoped;
+
+ // We're telling Wolverine that the IUserContext should always
+ // be pulled from HttpContext.RequestServices
+ // and this happens regardless of the ServerProviderSource!
+ opts.SourceServiceFromHttpContext();
+});
+
+return await app.RunJasperFxCommands(args);
+```
+snippet source | anchor
+
+
+Notice the call to `SourceServiceFromHttpContext()`. That directs Wolverine.HTTP to always pull the service
+`T` from the `HttpContext.RequestServices` scoped container so that Wolverine.HTTP can play nicely with custom AspNetCore
+middleware or whatever else you have around your Wolverine.HTTP endpoints.
+
+::: warning
+The Wolverine team believes that smuggling important state between upstream middleware and downstream handlers
+leads to code that is hard to reason about and hence, potentially buggy in real life usage. Alas, you could easily
+need this functionality in the real world, so here you go.
+:::
diff --git a/docs/guide/messaging/transports/signalr.md b/docs/guide/messaging/transports/signalr.md
index c22edb785..1aecce433 100644
--- a/docs/guide/messaging/transports/signalr.md
+++ b/docs/guide/messaging/transports/signalr.md
@@ -40,7 +40,12 @@ builder.UseWolverine(opts =>
// to wire SignalR services into Wolverine itself
// This does also call IServiceCollection.AddSignalR()
// to register DI services for SignalR as well
- opts.UseSignalR();
+ opts.UseSignalR(o =>
+ {
+ // Optionally configure the SignalR HubOptions
+ // for the WolverineHub
+ o.ClientTimeoutInterval = 10.Seconds();
+ });
// Using explicit routing to send specific
// messages to SignalR
@@ -54,7 +59,7 @@ builder.UseWolverine(opts =>
});
});
```
-snippet source | anchor
+snippet source | anchor
That handles the Wolverine configuration and the SignalR service registrations, but you will also need to map
@@ -79,7 +84,7 @@ app.MapWolverineSignalRHub("/api/messages");
return await app.RunJasperFxCommands(args);
```
-snippet source | anchor
+snippet source | anchor
## Messages and Serialization
@@ -138,6 +143,20 @@ the raw message being sent to the client and received from the browser. Here's a
}
```
+You can always preview the message type name by using the `dotnet run -- describe` command and finding the
+"Message Routing" table in that output, which should look like this from the sample application:
+
+```text
+ Message Routing
+┌───────────────────────────────┬────────────────────┬──────────────────────┬──────────────────┐
+│ .NET Type │ Message Type Alias │ Destination │ Content Type │
+├───────────────────────────────┼────────────────────┼──────────────────────┼──────────────────┤
+│ WolverineChat.ChatMessage │ chat_message │ signalr://wolverine/ │ application/json │
+│ WolverineChat.Ping │ ping │ signalr://wolverine/ │ application/json │
+│ WolverineChat.ResponseMessage │ response_message │ signalr://wolverine/ │ application/json │
+└───────────────────────────────┴────────────────────┴──────────────────────┴──────────────────┘
+```
+
The only elements that are mandatory are the `type` node that should be the Wolverine message type name and `data` that
is the actual message serialized by JSON. Wolverine will send the full CloudEvents envelope structure because it's
reusing the envelope mapping from [our CloudEvents interoperability](/tutorials/interop.html#interop-with-cloudevents), but the browser code **only** needs to send `type`
@@ -543,5 +562,3 @@ response back to the originating caller even if the work required intermediate s
behavior today is the usage of the `[EnlistInCurrentConnectionSaga]` that should be on either
-
-
diff --git a/docs/guide/migration.md b/docs/guide/migration.md
index 7e5dedd56..9c9ece91c 100644
--- a/docs/guide/migration.md
+++ b/docs/guide/migration.md
@@ -1,8 +1,29 @@
# Migration Guide
-## Key Changes in 4.0
+## Key Changes in 5.0
+
+5.0 had very few breaking changes in the public API, but some in "publinternals" types most users would never touch. The
+biggest change in the internals is the replacement of the venerable [TPL DataFlow library](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library)
+with the [System.Threading.Channels library](https://learn.microsoft.com/en-us/dotnet/core/extensions/channels)
+in every place that Wolverine uses in memory queueing. The only change this caused to the public API was the removal of
+the option for direct configuration of the TPL DataFlow `ExecutionOptions`. Endpoint ordering and parallelization options
+are unchanged otherwise in the public fluent interface for configuration.
+
+The `IntegrateWithWolverine()` syntax for ["ancillary stores"](/guide/durability/marten/ancillary-stores) changed to a [nested closure](https://martinfowler.com/dslCatalog/nestedClosure.html) syntax to be more consistent
+with the syntax for the main [Marten](https://martendb.io) store. The [Wolverine managed distribution of Marten projections and subscriptions](/guide/durability/marten/distribution)
+now applies to the ancillary stores as well.
+
+The new [Partitioned Sequential Messaging](/guide/messaging/partitioning) feature is a potentially huge step forward for
+building a Wolverine system that can efficiently and resiliently handle concurrent access to sensitive resources.
-4.0 had very few breaking changes.
+The [Aggregate Handler Workflow](/guide/durability/marten/event-sourcing) feature with Marten now supports strong typed identifiers.
+
+The declarative data access features with Marten (`[Aggregate]`, `[ReadAggregate]`, `[Entity]` or `[Document]`) can utilize
+Marten batch querying for better efficiency when a handler or HTTP endpoint uses more than one declaration for data loading.
+
+Better control over how [Wolverine generates code with respect to IoC container usage](/guide/codegen.html#wolverine-code-generation-and-ioc).
+
+## Key Changes in 4.0
* Wolverine dropped all support for .NET 6/7
* The previous dependencies on Oakton, JasperFx.Core, and JasperFx.CodeGeneration were all combined into a single [JasperFx](https://github.com/jasperfx/jasperfx) library. There are shims for any method with "Oakton" in its name, but these are marked as `[Obsolete]`. You can pretty well do a find and replace for "Oakton" to "JasperFx". If your Oakton command classes live in a different project than the runnable application, add this to that project's `Properties/AssemblyInfo.cs` file:
diff --git a/docs/guide/testing.md b/docs/guide/testing.md
index 88c04b355..38425bd0b 100644
--- a/docs/guide/testing.md
+++ b/docs/guide/testing.md
@@ -82,7 +82,7 @@ public async Task using_tracked_sessions()
overdrawn.AccountId.ShouldBe(debitAccount.AccountId);
}
```
-snippet source | anchor
+snippet source | anchor
The tracked session mechanism utilizes Wolverine's internal instrumentation to "know" when all the outstanding
@@ -141,6 +141,15 @@ public async Task using_tracked_sessions_advanced(IHost otherWolverineSystem)
// Again, this is testing against processes, with another IHost
.WaitForMessageToBeReceivedAt(otherWolverineSystem)
+
+ // Wolverine does this automatically, but it's sometimes
+ // helpful to tell Wolverine to not track certain message
+ // types during testing. Especially messages originating from
+ // some kind of polling operation
+ .IgnoreMessageType()
+
+ // Another option
+ .IgnoreMessagesMatchingType(type => type.CanBeCastTo())
// There are many other options as well
.InvokeMessageAndWaitAsync(debitAccount);
@@ -149,7 +158,7 @@ public async Task using_tracked_sessions_advanced(IHost otherWolverineSystem)
overdrawn.AccountId.ShouldBe(debitAccount.AccountId);
}
```
-snippet source | anchor
+snippet source | anchor
The samples shown above inlcude `Sent` message records, but there are more properties available in the `TrackedSession` object.
@@ -289,7 +298,7 @@ public class When_message_is_sent : IAsyncLifetime
public async Task DisposeAsync() => await _host.StopAsync();
}
```
-snippet source | anchor
+snippet source | anchor
As you can see, we just have to start our application, attach a tracked session to it, and then wait for the message to be published. This way, we can test the whole process of the application, from the file change to the message publication, in a single test.
@@ -433,7 +442,7 @@ public static IEnumerable