Skip to content
4,332 changes: 4,332 additions & 0 deletions pi-session-2026-09-03T07-32-04-213Z_01a0662e-95f5-7570-adde-5ca565b482e5.html

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public class MessageMutator : IMutateIncomingMessages
public Task MutateIncoming(MutateIncomingMessageContext context)
{
var original = (OriginalMessage)context.Message;
#pragma warning disable CS0618 // Deliberate coverage of the legacy runtime-type-routing setter until its removal
context.Message = new NewMessage { SomeId = original.SomeId };
#pragma warning restore CS0618
return Task.CompletedTask;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public Task MutateOutgoing(MutateOutgoingMessageContext context)
{
if (context.OutgoingMessage is V1Message)
{
#pragma warning disable CS0618 // Deliberate coverage of the legacy runtime-type-routing setter until its removal
context.OutgoingMessage = new V2Message();
#pragma warning restore CS0618
}
return Task.CompletedTask;
}
Expand Down
85 changes: 85 additions & 0 deletions src/NServiceBus.Core.Analyzer.Fixes/MessagingMigrationFixer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
continue;
}

if (node is AssignmentExpressionSyntax assignment &&
TryGetMutatorReplacementMethod(semanticModel, assignment, out var replacementMethodName))
{
context.RegisterCodeFix(
CodeAction.Create(
"Use the strongly typed message overload",
cancellationToken => ReplaceAssignmentWithTypedCall(
context.Document,
root,
assignment,
replacementMethodName,
messageType!,
cancellationToken),
EquivalenceKey),
diagnostic);
continue;
}

if (node.FirstAncestorOrSelf<InvocationExpressionSyntax>() is not { } invocation ||
!CanAddTypeArgument(invocation.Expression))
{
Expand Down Expand Up @@ -209,6 +227,73 @@ static Task<Document> AddTypeArgumentToMethodReference(
return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(methodReference, updatedMethodReference)));
}

static bool TryGetMutatorReplacementMethod(
SemanticModel? semanticModel,
AssignmentExpressionSyntax assignment,
out string methodName)
{
methodName = null!;
if (semanticModel is null || assignment.Left is not MemberAccessExpressionSyntax memberAccess)
{
return false;
}

var propertySymbol = semanticModel.GetSymbolInfo(memberAccess).Symbol;
if (propertySymbol is not IPropertySymbol
{
ContainingType: { } containingType
})
{
return false;
}

var containingTypeName = containingType.ToDisplayString();
if (propertySymbol.Name == "Message" &&
containingTypeName == "NServiceBus.MessageMutator.MutateIncomingMessageContext")
{
methodName = "UpdateMessageInstance";
return true;
}

if (propertySymbol.Name == "OutgoingMessage" &&
containingTypeName == "NServiceBus.MessageMutator.MutateOutgoingMessageContext")
{
methodName = "UpdateMessage";
return true;
}

return false;
}

static Task<Document> ReplaceAssignmentWithTypedCall(
Document document,
SyntaxNode root,
AssignmentExpressionSyntax assignment,
string methodName,
string messageType,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var memberAccess = (MemberAccessExpressionSyntax)assignment.Left;
var typeArgument = SyntaxFactory.ParseTypeName(messageType)
.WithAdditionalAnnotations(Simplifier.Annotation);
var typeArguments = SyntaxFactory.TypeArgumentList(
SyntaxFactory.SingletonSeparatedList(typeArgument));

var invocation = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
memberAccess.Expression,
SyntaxFactory.GenericName(SyntaxFactory.Identifier(methodName), typeArguments)
.WithTriviaFrom(memberAccess.Name)),
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(assignment.Right))))
.WithAdditionalAnnotations(Formatter.Annotation);

return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(assignment, invocation)));
}

static ExpressionSyntax AddTypeArgumentToExpression(ExpressionSyntax expression, string messageType)
{
var typeArgument = SyntaxFactory.ParseTypeName(messageType)
Expand Down
Loading