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!