MassTransit: F# anonymous objects don't work (message type must be concrete and have members corresponding to the message interface)
Contact Details
No response
Version
8.x
On which operating system(s) are you experiencing the issue?
Windows
Using which broker(s) did you encounter the issue?
Azure Service Bus, In-Memory
What are the steps required to reproduce the issue?
Please see the attached repro solution:
Code below for reference:
namespace Repro
open System
open System.Threading
open System.Threading.Tasks
open System.Text.Json
open MassTransit
type MyMessage =
abstract member Data: int
type MyMessageImplWithoutMember(data) =
interface MyMessage with
member _.Data = data
type MyMessageImplWithMember(data) =
member _.Data = data
interface MyMessage with
member _.Data = data
type MyMessageConsumer() =
interface IConsumer<MyMessage> with
member _.Consume(context) =
Console.WriteLine($"Received: {JsonSerializer.Serialize(context.Message)}")
Task.CompletedTask
module Test =
let bus =
Bus.Factory.CreateUsingInMemory(fun cfg ->
cfg.ReceiveEndpoint(
"queue",
fun (epCfg: IInMemoryReceiveEndpointConfigurator) ->
epCfg.Consumer(fun () -> MyMessageConsumer())
))
bus.Start()
let publishWithSerializedLog (msg: MyMessage) =
Console.WriteLine($"Sending {msg.GetType().Name}: {JsonSerializer.Serialize(msg)}")
bus.Publish<MyMessage>(msg).GetAwaiter().GetResult()
publishWithSerializedLog
// This is the F# anonymous object/interface syntax
{ new MyMessage with
member _.Data = 100 }
Thread.Sleep(1000)
publishWithSerializedLog (MyMessageImplWithoutMember(200))
Thread.Sleep(1000)
publishWithSerializedLog (MyMessageImplWithMember(300))
Thread.Sleep(1000)
MassTransit advocates instantiating messages using anonymous objects. Strangely, this does not work in F#. The only thing that works in F#, is sending a concrete message type that not only implements the interface, but also contains identically named regular members (F# interfaces are always explicitly implemented, as opposed to C#, where interfaces are normally implicitly implemented).
System.Text.Json can serialize all alternatives just fine, as shown in the output, so I’m not sure why this doesn’t work.
The weirdest thing is that if I debug and step through, and put a breakpoint at this line:
Then look at what I got in the interactive window:
Object.ReferenceEquals(_context.Message, envelope.Message)
true
System.Text.Json.JsonSerializer.Serialize(_context.Message)
"{\"Data\":100}"
System.Text.Json.JsonSerializer.Serialize(envelope.Message)
"{}"
That just doesn’t make sense. It’s one and the same object, but the serialized output is different.
What is the expected behavior?
Sending clo@53: {"Data":100}
Received: {"Data":100}
Sending MyMessageImplWithoutMember: {"Data":200}
Received: {"Data":200}
Sending MyMessageImplWithMember: {"Data":300}
Received: {"Data":300}
What actually happened?
Sending clo@53: {"Data":100}
Received: {"Data":0}
Sending MyMessageImplWithoutMember: {"Data":200}
Received: {"Data":0}
Sending MyMessageImplWithMember: {"Data":300}
Received: {"Data":300}
Related log output, including any exceptions
No response
Link to repository that demonstrates/reproduces the issue
No response
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 16 (16 by maintainers)
I will leave this and move to using records instead. Thanks for all the help!