Skip to content

MultipleHandlerBehavior.Separated with handler handling multiple messages overwrite generated handler #2004

@JosephMarotte

Description

@JosephMarotte

Describe the bug
Using opts.MultipleHandlerBehavior = MultipleHandlerBehavior.Separated;
With a Handler handling multiple separated messages leads to invalid code generation (and file being overwriten)

To Reproduce
Add the following test to wolverine/src/Testing/CoreTests

using System.Diagnostics;
using CoreTests.Bugs;
using JasperFx.CodeGeneration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Wolverine.Tracking;
using Xunit;

namespace CoreTests.Bugs
{
    public class Bug_XXX
    {
        [Fact]
        public async Task multiple_handler_file_overwrite()
        {
            using var host = await Host.CreateDefaultBuilder()
                .UseWolverine(opts =>
                {
                    opts.CodeGeneration.TypeLoadMode = TypeLoadMode.Auto;
                    opts.MultipleHandlerBehavior = MultipleHandlerBehavior.Separated;
                })
                .StartAsync();

            await host.InvokeMessageAndWaitAsync(new SayStuff0());
        }
    }

    public record SayStuff0();

    public record SayStuff1(string Text);
    public record SayStuff2(string Text);

    public class BSayStuffHandler
    {

        public (SayStuff1, SayStuff2) Handle(SayStuff0 _)
        {
            return (new SayStuff1("Hello"), new SayStuff2("World"));
        }
        public void Handle(SayStuff1 stuff) => Debug.WriteLine(stuff.Text);
        public void Handle(SayStuff2 stuff) => Debug.WriteLine(stuff.Text);
    }

    public class ASayStuffHandler
    {
        public void Handle(SayStuff1 stuff) => Debug.WriteLine(stuff.Text);
        public void Handle(SayStuff2 stuff) => Debug.WriteLine(stuff.Text);
    }
}

Run dotnet test --framework "net9.0" --filter "FullyQualifiedName=CoreTests.Bugs.Bug_XXX.multiple_handler_file_overwrite"

Watch output generated/files

Expected behavior
5 handler are created :

  • 1 for SayStuff0
  • 2 for SayStuff1
  • 2 for SaySutff2

Actual behavior

  • Because of naming conflict, the handler for SayStuff1 are overwritten :
Generated code to /wolverine/src/Testing/CoreTests/Internal/Generated/WolverineHandlers/SayStuff0Handler721297504.cs
Generated code to /wolverine/src/Testing/CoreTests/Internal/Generated/WolverineHandlers/ASayStuffHandlerHandler941180967.cs
info: CoreTests.Bugs.SayStuff1[104]
      Successfully processed message CoreTests.Bugs.SayStuff1#08de4c6e-e776-240e-ca82-b5712b550000 from local://coretests.bugs.asaystuffhandler/
Generated code to /wolverine/src/Testing/CoreTests/Internal/Generated/WolverineHandlers/BSayStuffHandlerHandler1158313364.cs
info: CoreTests.Bugs.SayStuff1[104]
      Successfully processed message CoreTests.Bugs.SayStuff1#08de4c6e-e776-1aa4-ca82-b5712b550000 from local://coretests.bugs.bsaystuffhandler/
Generated code to /wolverine/src/Testing/CoreTests/Internal/Generated/WolverineHandlers/BSayStuffHandlerHandler1158313364.cs
info: CoreTests.Bugs.SayStuff2[104]
      Successfully processed message CoreTests.Bugs.SayStuff2#08de4c6e-e776-4150-ca82-b5712b550000 from local://coretests.bugs.bsaystuffhandler/
Generated code to /wolverine/src/Testing/CoreTests/Internal/Generated/WolverineHandlers/ASayStuffHandlerHandler941180967.cs

Notice how we write twice to ASayStuffHandlerHandler941180967

File content for more context :

// <auto-generated/>
#pragma warning disable

namespace Internal.Generated.WolverineHandlers
{
    // START: ASayStuffHandlerHandler941180967
    [global::System.CodeDom.Compiler.GeneratedCode("JasperFx", "1.0.0")]
    public sealed class ASayStuffHandlerHandler941180967 : Wolverine.Runtime.Handlers.MessageHandler
    {


        public override System.Threading.Tasks.Task HandleAsync(Wolverine.Runtime.MessageContext context, System.Threading.CancellationToken cancellation)
        {
            // The actual message body
            var sayStuff2 = (CoreTests.Bugs.SayStuff2)context.Envelope.Message;

            System.Diagnostics.Activity.Current?.SetTag("message.handler", "CoreTests.Bugs.ASayStuffHandler");
            var aSayStuffHandler = new CoreTests.Bugs.ASayStuffHandler();
            
            // The actual message execution
            aSayStuffHandler.Handle(sayStuff2);

            return System.Threading.Tasks.Task.CompletedTask;
        }

    }

    // END: ASayStuffHandlerHandler941180967
    
    
}
// <auto-generated/>
#pragma warning disable

namespace Internal.Generated.WolverineHandlers
{
    // START: BSayStuffHandlerHandler1158313364
    [global::System.CodeDom.Compiler.GeneratedCode("JasperFx", "1.0.0")]
    public sealed class BSayStuffHandlerHandler1158313364 : Wolverine.Runtime.Handlers.MessageHandler
    {


        public override System.Threading.Tasks.Task HandleAsync(Wolverine.Runtime.MessageContext context, System.Threading.CancellationToken cancellation)
        {
            // The actual message body
            var sayStuff2 = (CoreTests.Bugs.SayStuff2)context.Envelope.Message;

            System.Diagnostics.Activity.Current?.SetTag("message.handler", "CoreTests.Bugs.BSayStuffHandler");
            var bSayStuffHandler = new CoreTests.Bugs.BSayStuffHandler();
            
            // The actual message execution
            bSayStuffHandler.Handle(sayStuff2);

            return System.Threading.Tasks.Task.CompletedTask;
        }

    }

    // END: BSayStuffHandlerHandler1158313364
    
    
}

Additional context
Root cause seems to depends on the naming of the generated file which is not unique when two message in the same handler generate a separated handler (as the documentation indicates https://wolverinefx.net/guide/handlers/#multiple-handlers-for-the-same-message-type)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions