diff --git a/docs/guide/messaging/message-bus.md b/docs/guide/messaging/message-bus.md index a524e1c03..6eb7d0649 100644 --- a/docs/guide/messaging/message-bus.md +++ b/docs/guide/messaging/message-bus.md @@ -87,6 +87,12 @@ case, Wolverine does enforce timeout conditions with a default of 5 seconds whic ## Request/Reply +::: warning +There was a breaking change in behavior for this functionality in Wolverine 3.0. The response type, the `T` in `InvokeAsync()` is **not** +sent as a cascaded message if that type is the requested response type. You will have to explicitly send the response out through `IMessageBus.PublishAsync()` +to force that to be sent out instead of just being the response. +::: + Wolverine also has direct support for the [request/reply](https://www.enterpriseintegrationpatterns.com/RequestReply.html) pattern or really just mediating between your code and complex query handlers through the `IMessageBus.InvokeAsync()` API. To make that concrete, let's assume you want to request the results of a mathematical operation as shown below in these message types and a corresponding message handler: @@ -127,6 +133,10 @@ public async Task invoke_math_operations(IMessageBus bus) Note that this API hides whether or not this operation is a local operation running on the same thread and invoking a local message handler or sending a message through to a remote endpoint and waiting for the response. The same timeout mechanics and performance concerns apply to this operation as the `InvokeAsync()` method described in the previous section. +Note that if you execute the `Numbers` message from above with `InvokeAsync()`, the `Results` response will only be +returned as the response and will not be published as a message. This was a breaking change in Wolverine 3.0. We think (hope) +that this will be less confusing. + ## Sending or Publishing Messages [Publish/Subscribe](https://docs.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber) is a messaging pattern where the senders of messages do not need to specifically know what the specific subscribers are for a given message. In this case, some kind of middleware or infrastructure is responsible for either allowing subscribers to express interest in what messages they need to receive or apply routing rules to send the published messages to the right places. Wolverine's messaging support was largely built to support the publish/subscribe messaging pattern. diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 216182a16..5c8636e04 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -47,3 +47,7 @@ builder.Services.AddWolverineHttp(); Also for Wolverine.Http users, the `[Document]` attribute behavior in the Marten integration is now "required by default." + +The behavior of `IMessageBus.InvokeAsync(message)` changed in 3.0 such that the `T` response **is not also published as a +message** at the same time when the initial message is sent with request/response semantics. Wolverine has gone back and forth +in this behavior in its life, but at this point, the Wolverine thinks that this is the least confusing behavioral rule. \ No newline at end of file diff --git a/src/Testing/CoreTests/Acceptance/local_invoke_also_publishes_the_return_value.cs b/src/Testing/CoreTests/Acceptance/local_invoke_does_not_publish_the_return_value.cs similarity index 69% rename from src/Testing/CoreTests/Acceptance/local_invoke_also_publishes_the_return_value.cs rename to src/Testing/CoreTests/Acceptance/local_invoke_does_not_publish_the_return_value.cs index ee37944b0..341903eea 100644 --- a/src/Testing/CoreTests/Acceptance/local_invoke_also_publishes_the_return_value.cs +++ b/src/Testing/CoreTests/Acceptance/local_invoke_does_not_publish_the_return_value.cs @@ -5,10 +5,10 @@ namespace CoreTests.Acceptance; -public class local_invoke_also_publishes_the_return_value +public class local_invoke_does_not_publish_the_return_value { [Fact] - public async Task should_also_publish_the_return_value_when_invoking_locally() + public async Task should_not_publish_the_return_value_when_invoking_locally() { using var host = await Host.CreateDefaultBuilder() .UseWolverine().StartAsync(); @@ -18,11 +18,7 @@ public async Task should_also_publish_the_return_value_when_invoking_locally() response.Name.ShouldBe(name); - tracked.Sent.SingleMessage() - .Name.ShouldBe(name); - - tracked.Sent.SingleMessage().Id.ShouldBe(response.Id); - tracked.Sent.SingleMessage().Id.ShouldBe(response.Id); + tracked.Sent.MessagesOf().Any().ShouldBeFalse(); } } diff --git a/src/Wolverine/Runtime/Handlers/Executor.cs b/src/Wolverine/Runtime/Handlers/Executor.cs index 1db663bd2..fd54cc13c 100644 --- a/src/Wolverine/Runtime/Handlers/Executor.cs +++ b/src/Wolverine/Runtime/Handlers/Executor.cs @@ -122,7 +122,8 @@ public async Task InvokeAsync(object message, MessageBus bus, Cancellation ReplyUri = TransportConstants.RepliesUri, ReplyRequested = typeof(T).ToMessageTypeName(), ResponseType = typeof(T), - TenantId = tenantId ?? bus.TenantId + TenantId = tenantId ?? bus.TenantId, + DoNotCascadeResponse = true }; bus.TrackEnvelopeCorrelation(envelope, Activity.Current); diff --git a/src/Wolverine/Runtime/MessageContext.cs b/src/Wolverine/Runtime/MessageContext.cs index 01527365d..caf498d07 100644 --- a/src/Wolverine/Runtime/MessageContext.cs +++ b/src/Wolverine/Runtime/MessageContext.cs @@ -362,7 +362,7 @@ public async Task EnqueueCascadingAsync(object? message) } if (Envelope?.ResponseType != null && (message?.GetType() == Envelope.ResponseType || - Envelope.ResponseType.IsAssignableFrom(message?.GetType()))) + Envelope.ResponseType.IsInstanceOfType(message))) { Envelope.Response = message;