JSON deserialization error when not using shared class library for messages

Hi,

I am trying to use JSON serialization without a shared class library. Published and subscribed classes have the same name, implement IEvent and are in the same namespace, but different assemblies. The JSON serializer has a default setting (no type name handling enabled), this is 7.x. I get the following error:

NServiceBus.RecoverabilityExecutor[0]
      Moving message '6a6a7d54-c75c-4af2-8708-ab8f01136699' to the error queue 'error' because processing failed due to an exception:
NServiceBus.MessageDeserializationException: An error occurred while attempting to extract logical messages from incoming physical message 6a6a7d54-c75c-4af2-8708-ab8f01136699
 ---> System.Exception: Could not find metadata for 'Newtonsoft.Json.Linq.JObject'.
Ensure the following:
1. 'Newtonsoft.Json.Linq.JObject' is included in initial scanning.
2. 'Newtonsoft.Json.Linq.JObject' implements either 'IMessage', 'IEvent' or 'ICommand' or alternatively, if you don't want to implement an interface, you can use 'Unobtrusive Mode'.
   at NServiceBus.Unicast.Messages.MessageMetadataRegistry.GetMessageMetadata(Type messageType)
   at NServiceBus.Pipeline.LogicalMessageFactory.Create(Type messageType, Object message)
   at NServiceBus.DeserializeMessageConnector.Extract(IncomingMessage physicalMessage)
   at NServiceBus.DeserializeMessageConnector.ExtractWithExceptionHandling(IncomingMessage message)
   --- End of inner exception stack trace ---
   at NServiceBus.DeserializeMessageConnector.ExtractWithExceptionHandling(IncomingMessage message)
   at NServiceBus.DeserializeMessageConnector.Invoke(IIncomingPhysicalMessageContext context, Func`2 stage)
   at NServiceBus.UnitOfWorkBehavior.InvokeUnitsOfWork(IIncomingPhysicalMessageContext context, Func`2 next)
   at NServiceBus.Transport.AzureServiceBus.TransactionScopeSuppressBehavior.Invoke(IIncomingPhysicalMessageContext context, Func`1 next)
   at NServiceBus.MutateIncomingTransportMessageBehavior.InvokeIncomingTransportMessagesMutators(IIncomingPhysicalMessageContext context, Func`2 next)
   at NServiceBus.ProcessingStatisticsBehavior.Invoke(IIncomingPhysicalMessageContext context, Func`2 next)
   at NServiceBus.TransportReceiveToPhysicalMessageConnector.Invoke(ITransportReceiveContext context, Func`2 next)
   at NServiceBus.MainPipelineExecutor.Invoke(MessageContext messageContext)
   at NServiceBus.Transport.AzureServiceBus.MessagePump.InnerProcessMessage(Task`1 receiveTask)

note that the problem does not occur with XML serialization or a shared class library.

Hi Marco,

You may also need to add a behavior to remove assembly qualified information from the message headers for this scenario to work.

https://docs.particular.net/samples/consumer-driven-contracts/#full-names-instead-of-fully-qualified-assembly-names

Kind regards,
Yves

Thanks! That worked. I had gotten the impression that there was an automatic fallback when there was no match an assembly qualified name. Anyways, adding this to the publisher made it work.

There is actually one additional problem that showed up: we are requiring bridging from ASB to another transport. The nServiceBus.Router appears to blow up on the runtime type and the pipe line for the endpoint appears to not be exposed. @SzymonPobiega : is there a way to use full names instead of fully qualified assembly names with the router?

That’s interesting. The Router should not try to instantiate the Type objects. Can you paste the stack trace so that I can try to reproduce this problem?

Here is the stack trace. The runtime type generator has the issue.
2020-04-08 09:54:43.865 WARN The circuit breaker for Router.MsqToAsb is now in the armed state
2020-04-08 09:54:45.159 WARN The circuit breaker for Router.MsqToAsb will now be triggered
2020-04-08 09:54:45.161 ERROR Persistent error while processing messages in Router.MsqToAsb. Entering throttled mode.
System.IO.FileLoadException: The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
at System.Reflection.AssemblyName.nInit(RuntimeAssembly& assembly, Boolean forIntrospection, Boolean raiseResolveEvent)
at System.Reflection.AssemblyName…ctor(String assemblyName)
at NServiceBus.Router.RuntimeTypeGenerator.GetType(String messageType)
at PublishPreroutingTerminator.<>c__DisplayClass3_0.<b__1>d.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at PublishPreroutingTerminator.<Terminate>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult()
at NServiceBus.Router.ChainTerminator1.<Invoke>d__1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at NServiceBus.Router.TerminatorInvocationRule1.d__2.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at PreroutingToPublishPreroutingFork.d__0.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at PreroutingToSendPreroutingFork.d__0.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at PreroutingToSubscribePreroutingFork.d__0.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at ThrottlingRawEndpointConfig`1.<>c__DisplayClass1_0.<b__1>d.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NServiceBus.Transport.AzureServiceBus.MessagePump.d__22.MoveNext()

Hi

Thanks! Found it. The Router assumes the name is assembly-qualified. It does not require the type to be present but requires the name of the assembly. The name is not used by the router so I’ll remove that requirement. Here’s the issue https://github.com/SzymonPobiega/NServiceBus.Router/issues/28.

I’ll ping you one the fix is released.

Router 3.7.1 with the fix is on NuGet. One thing to note is it now supports non-qualified types names e.g. MyNamespace.MyClass but it does not support empty assembly names e.g. MyNamespace.MyClass, . In the latter case it will still throw exception.

Szymon

Thanks for the quick turn around!

Hi,

Just got back to testing this, still seeing an error. I think because it really wants valid version info (version info is part of the message type). It appears to blow up when trying to create the enumerated message types for the subscription storage.

2020-06-18 19:01:36.430 ERROR Persistent error while processing messages in Router.MsqToAsb. Entering throttled mode. 

System.FormatException: Input string was not in a correct format.
at System.Version.VersionResult.SetFailure(ParseFailureKind failure, String argument)
at System.Version.TryParseComponent(String component, String componentName, VersionResult& result, Int32& parsedComponent)
at System.Version.TryParseVersion(String version, VersionResult& result)
at System.Version.Parse(String input)
at NServiceBus.Unicast.Subscriptions.MessageType…ctor(String messageTypeString)
at ForwardPublishStorageDrivenRule.<>c.b__3_0(String t)
at System.Linq.Enumerable.WhereSelectArrayIterator2.MoveNext() at TestEnv.Router.InMemorySubscriptionStorage.GetSubscriberAddressesForMessage(IEnumerable1 messageTypes, ContextBag context)
at ForwardPublishStorageDrivenRule.d__3.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at NServiceBus.Router.ChainTerminator1.d__1.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at NServiceBus.Router.TerminatorInvocationRule1.<Invoke>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at PublishPreroutingTerminator.<>c__DisplayClass3_0.<<Terminate>b__1>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult()
at PublishPreroutingTerminator.d__3.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at NServiceBus.Router.ChainTerminator1.d__1.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at NServiceBus.Router.TerminatorInvocationRule1.<Invoke>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at PreroutingToPublishPreroutingFork.<Invoke>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at PreroutingToSendPreroutingFork.<Invoke>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at PreroutingToSubscribePreroutingFork.<Invoke>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() at ThrottlingRawEndpointConfig1.<>c__DisplayClass1_0.<b__1>d.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NServiceBus.Transport.AzureServiceBus.MessagePump.d__23.MoveNext()

This entry in the stack trace suggests it blows when routing a published event when trying to figure out the type of the event in order to load the list of subscribers. What exactly is there in the NServiceBus.EnclosedMessageTypes header?

Hi, not having actual assembly info in the message type seemed to cause issues across the stack. It for example also threw an exception when receiving a command, but then the command was still handled correctly. I finally decided to follow the same approach you followed: putting a fake assembly name in and then I handle the type resolution with a mutator whenever I see the fake assembly name. That provides me the functionally I wanted without causing issues.