Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Snippets/Core/Core_10/Core_10.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.*" />
<PackageReference Include="Newtonsoft.Json" Version="13.*" />
<PackageReference Include="NServiceBus" Version="10.*" />
<PackageReference Include="NServiceBus" Version="10.3.0-alpha.3" />
<PackageReference Include="NServiceBus.Callbacks" Version="6.*" />
<PackageReference Include="NServiceBus.ClaimCheck" Version="2.*" />
<PackageReference Include="NServiceBus.Encryption.MessageProperty" Version="6.*" />
Expand Down
2 changes: 1 addition & 1 deletion Snippets/Core/Core_10/Headers/Writers/HeaderWriterSaga.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public async Task Handle(SendFromSagaMessage message, IMessageHandlerContext con
var replyFromSagaMessage = new ReplyFromSagaMessage();
await context.Reply(replyFromSagaMessage);
var replyToOriginatorFromSagaMessage = new ReplyToOriginatorFromSagaMessage();
await ReplyToOriginator(context, replyToOriginatorFromSagaMessage);
await ReplyToOriginator<ReplyToOriginatorFromSagaMessage>(context, replyToOriginatorFromSagaMessage);
await RequestTimeout(context, TimeSpan.FromMilliseconds(1), new TimeoutFromSaga());
}

Expand Down
39 changes: 36 additions & 3 deletions Snippets/Core/Core_10/Hosting/StartUpDiagnostics.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Core.Hosting;

using System.Text.Json.Serialization;
using System.Threading.Tasks;
using NServiceBus;
using NServiceBus.Settings;
Expand Down Expand Up @@ -44,12 +45,44 @@ void CustomDiagnosticsSection(IReadOnlySettings settings)

settings.AddStartupDiagnosticsSection(
sectionName: "MySection",
section: new
section: new MyDiagnostics
{
SomeSetting = "some data",
SomeOtherSetting = 10
});
},
typeInfo: DiagnosticsJsonContext.Default.MyDiagnostics);

#endregion
}
}

void CustomDiagnosticsSectionFactory(IReadOnlySettings settings)
{
#region CustomDiagnosticsSectionFactory

settings.AddStartupDiagnosticsSectionFactory(
sectionName: "MySection",
sectionFactory: () => new MyDiagnostics
{
SomeSetting = "some data",
SomeOtherSetting = 10
},
typeInfo: DiagnosticsJsonContext.Default.MyDiagnostics);

#endregion
}
}

#region CustomDiagnosticsSectionTypes

sealed class MyDiagnostics
{
public required string SomeSetting { get; init; }
public required int SomeOtherSetting { get; init; }
}

[JsonSerializable(typeof(MyDiagnostics))]
sealed partial class DiagnosticsJsonContext : JsonSerializerContext
{
}

#endregion
2 changes: 1 addition & 1 deletion Snippets/Core/Core_10/Hosting/StartUpDiagnosticsWriter.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Snippet is code generated by StartUpDiagnosticsWriter.cs
startcode StartUpDiagnosticsWriter
{ "Container": { "Type": "external" }, "Endpoint": { "Name": "StartUpDiagnosticsWriter", "SendOnly": false, "NServiceBusVersion": "10.2.0" }, "Features": [ { "Name": "NServiceBus.ReceiveStatisticsFeature", "Enabled": false, "Active": true, "PrerequisiteStatus": { "IsSatisfied": true, "Reasons": [] }, "Dependencies": [], "Version": "10.2.0",
{ "Container": { "Type": "external" }, "Endpoint": { "Name": "StartUpDiagnosticsWriter", "SendOnly": false, "NServiceBusVersion": "10.3.0" }, "Features": [ { "Name": "NServiceBus.ReceiveStatisticsFeature", "Enabled": false, "Active": true, "PrerequisiteStatus": { "IsSatisfied": true, "Reasons": [] }, "Dependencies": [], "Version": "10.3.0",
...
endcode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public Task MutateIncoming(MutateIncomingMessageContext context)
var headers = context.Headers;

// the incoming message
// optionally replace the message instance by setting context.Message
// optionally replace the message instance with context.UpdateMessageInstance<T>(...)
var message = context.Message;

return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public Task MutateOutgoing(MutateOutgoingMessageContext context)
}

// the outgoing message
// optionally replace the message instance by setting context.OutgoingMessage
// optionally replace the message instance with context.UpdateMessage<T>(...)
var outgoingMessage = context.OutgoingMessage;

return Task.CompletedTask;
Expand Down
2 changes: 1 addition & 1 deletion Snippets/Core/Core_10/Sagas/Reply/MySaga.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public Task Handle(StartMessage message, IMessageHandlerContext context)
{
SomeId = Data.SomeId
};
return ReplyToOriginator(context, almostDoneMessage);
return ReplyToOriginator<AlmostDoneMessage>(context, almostDoneMessage);
}

#endregion
Expand Down
4 changes: 2 additions & 2 deletions Snippets/Core/Core_10/Sagas/Timeouts/MySaga.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ public Task Handle(Message2 message, IMessageHandlerContext context)
{
SomeId = Data.SomeId
};
return ReplyToOriginator(context, almostDoneMessage);
return ReplyToOriginator<AlmostDoneMessage>(context, almostDoneMessage);
}

public Task Timeout(MyCustomTimeout state, IMessageHandlerContext context)
{
if (!Data.Message2Arrived)
{
return ReplyToOriginator(context, new TiredOfWaitingForMessage2());
return ReplyToOriginator<TiredOfWaitingForMessage2>(context, new TiredOfWaitingForMessage2());
}
return Task.CompletedTask;
}
Expand Down
9 changes: 9 additions & 0 deletions Snippets/Core/Core_10/Scanning/ManualRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ void RegisterInstallerManually(EndpointConfiguration endpointConfiguration)

#endregion
}

void RegisterMessageTypeManually(EndpointConfiguration endpointConfiguration)
{
#region RegisterMessageTypeManually

endpointConfiguration.AddMessageType<PlaceOrder>();

#endregion
}
}

public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
Expand Down
2 changes: 1 addition & 1 deletion Snippets/Testing/Testing_10/Saga/MySaga.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class MySaga :
{
public async Task Handle(StartsSaga message, IMessageHandlerContext context)
{
await ReplyToOriginator(context, new MyResponse());
await ReplyToOriginator<MyResponse>(context, new MyResponse());
await context.Publish(new MyEvent());
await context.Send(new MyCommand());
await RequestTimeout(context, TimeSpan.FromDays(7), message);
Expand Down
2 changes: 1 addition & 1 deletion Snippets/Testing/Testing_10/Testing_10.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
<PackageReference Include="NServiceBus" Version="10.*" />
<PackageReference Include="NServiceBus" Version="10.3.0-alpha.3" />
<PackageReference Include="NServiceBus.Testing" Version="10.*" />
<PackageReference Include="NUnit" Version="4.*" />
<PackageReference Include="NUnit.Analyzers" Version="4.*" />
Expand Down
2 changes: 2 additions & 0 deletions menu/menu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@
Title: Non-Durable Messaging
- Url: nservicebus/messaging/forwarding
Title: Forwarding
- Url: nservicebus/messaging/trimming-safe-messaging-overloads
Title: Trimming-safe messaging overloads
- Url: nservicebus/messaging/delayed-delivery
Title: Delayed Delivery
Articles:
Expand Down
2 changes: 1 addition & 1 deletion nservicebus/handlers-and-sagas-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Registering Handlers and Sagas
summary: How to register message handlers and sagas with an NServiceBus endpoint.
component: Core
reviewed: 2026-04-27
reviewed: 2026-09-11
---

Registration tells an endpoint which message handlers and sagas to include. NServiceBus supports explicit registration (which is recommended for new endpoints) and automatic discovery via assembly scanning (which is useful for plugin or dynamic discovery scenarios).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ The source generator automatically matches the visibility of the generated exten

When source generation is not available or when only a few components need to be registered, individual types can be added explicitly.

> [!NOTE]
> `AddHandler<THandler>()`, `AddSaga<TSaga>()`, and `AddMessageType<T>()` use reflection unless the NServiceBus source generator replaces the call at build time. The source generator can only replace calls that name a concrete type, so pass handler, saga, and message types directly instead of forwarding a type parameter through generic helper code. Without a replacement, the call falls back to reflection and can report the trimming warning IL2026; `AddHandler<THandler>()` and `AddSaga<TSaga>()` can also report the AOT warning IL3050.

### Register a message handler

Use `AddHandler<THandler>()` to register a message handler:
Expand Down Expand Up @@ -100,6 +103,8 @@ snippet: DisableAssemblyScanning

When assembly scanning is disabled, message handlers, sagas, features, and installers must be explicitly registered. Messages received without a registered handler or saga [will fail and be moved to the error queue](/nservicebus/handlers/?version=core_10#no-handler-for-a-message).

In trimmed or Native AOT deployments, message types that are only sent, published, or replied to, and that no registered handler or saga handles, must also be registered explicitly with `AddMessageType<T>()`, available starting in NServiceBus version 10.3. See [registering message types](/nservicebus/messaging/trimming-safe-messaging-overloads.md#registering-message-types).

### Fine-grained scanning configuration

Scanning can be configured with exclusions, additional paths, nested directories, and exception handling. See [Assembly scanning](/nservicebus/hosting/assembly-scanning.md) for all configuration options.
Expand Down
6 changes: 2 additions & 4 deletions nservicebus/hosting/startup-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Startup diagnostics
summary: Describes the mechanism for gathering diagnostic information when endpoints start
component: Core
versions: '[7,)'
reviewed: 2026-05-07
reviewed: 2026-08-24
---

> [!NOTE]
Expand Down Expand Up @@ -41,8 +41,6 @@ snippet: CustomDiagnosticsWriter

## Adding startup diagnostics sections

To extend the startup diagnostics with custom sections:

snippet: CustomDiagnosticsSection
partial: adding-sections

Settings can be accessed from a [feature](/nservicebus/pipeline/features.md#feature-setup) or via the [endpoint configuration](/nservicebus/pipeline/features.md#feature-settings-endpointconfiguration).
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
To extend the startup diagnostics with custom sections:

snippet: CustomDiagnosticsSection
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
To extend the startup diagnostics with custom sections:

snippet: CustomDiagnosticsSection

Starting in version 10.3, custom sections can be registered with a strongly-typed value and its `JsonTypeInfo<T>`. The section value must be a named type registered with a source-generated `JsonSerializerContext`:

snippet: CustomDiagnosticsSectionTypes

Registering sections with type information makes startup diagnostics serialization AOT-safe and trimming-safe. This is required when reflection-based serialization is disabled, such as in Native AOT applications. In that case, a section registered with the object-based overload cannot be serialized. NServiceBus logs an error identifying the section, and the diagnostics document is not written. When reflection-based serialization is disabled, every section in the document must be registered with type information. A single legacy section prevents the complete document from being written.

The object-based overload remains available and continues to work when reflection-based serialization is enabled.

Use the factory overload when the diagnostics value is expensive to compute and should only be evaluated when the diagnostics are actually written. The factory is evaluated once, when the diagnostics document is written, and the resulting value is reused for every output target, including the log, the file, and any custom diagnostics writer. For cheap values, prefer the direct overload:

snippet: CustomDiagnosticsSectionFactory
3 changes: 2 additions & 1 deletion nservicebus/messaging/messages-events-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
title: Messages, events, and commands
summary: Messages as commands or events are the the unit of communication for message-based distributed systems. NServiceBus ensures they are used correctly.
component: Core
reviewed: 2025-02-19
reviewed: 2026-08-24
related:
- nservicebus/messaging/conventions
- nservicebus/messaging/unobtrusive-mode
- nservicebus/messaging/trimming-safe-messaging-overloads
- samples/message-assembly-sharing
redirects:
- nservicebus/introducing-ievent-and-icommand
Expand Down
Loading
Loading