I have an Azure v3 Function application (.NET Core 3.1 - Consumption plan) which has multiple Service Bus-triggered functions. Each function calls a method in different Event Handling Classes passing a message entity as a parameter. Both the event handling class and the different message classes belong to same dll (VS project), let's call it MyCompany.Domain.dll.
For some reason when processing high volume of messages I get this error where AllOpenSagasCleared is one message type (event). Notice number of DLL's loaded:
****** In Exception - Number of MyNamespace.Domain DLLs Loaded: 9 ******
Error processing MyNamespace.Events.AllOpenSagasCleared for MyNamespace.Validations.ClientTaskProgress ---> System.ArgumentException: Object of type 'MyNamespace.Guided.Tasks.Events.AllOpenSagasCleared' cannot be converted to type 'MyNamespace.Guided.Tasks.Events.AllOpenSagasCleared'. at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at MyCompany.Infrastructure.Events.TypedEventModel.PlayEvent(Object event) in C:\BuildAgent\work\2f3c308f09a9afe2\GuidedApi\MyCompany.Domain\Infrastructure\Events\TypeEventModel.cs:line 67
This is the code in the event handling base class ClientTaskProgress
(abstract) that calls the method in that same class through reflection passing the message (event) object as parameter:
var method = GetType().GetMethod("Handle", new[] { @event.GetType() }); ;
method.Invoke(this, new[] { @event});
The exception comes from the Invoke call above. After adding some logging, I found that MyCompany.Domain.dll assembly is being loaded multiple times under the Function Context AppDomain.CurrentDomain.GetAssemblies
.
Reading this documentation: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext?view=netcore-3.1 It says:
The same assembly is loaded multiple times into different contexts.
- This can lead to confusing error messages, for example "Unable to cast object of type 'Sample.Plugin' to type 'Sample.Plugin'".
- Marshaling across isolation boundaries is non-trivial. A typical solution is to use an interface defined in an assembly that's only loaded into the default load context.
Could someone please provide an example of what they mean by: "A typical solution is to use an interface defined in an assembly that's only loaded into the default load context."?
Here is the oversimplified structure of my classes belonging to MyNamespace.Domain.dll
:
public class ClientTaskProgress: TypeEventModel, IHandle<AllOpenSagasCleared>, IHandle<ResetTask>
{
public void Handle(AllOpenSagasCleared message)
{
//Trivial code
}
public void Handle(ResetTask message)
{
//Trivial code
}
}
public abstract class TypeEventModel
{
public void PlayEvent(object @event)
{
var method = GetType().GetMethod("Handle", new[] { @event.GetType() }); ;
method.Invoke(this, new[] { @event});
}
}
public class AllOpenSagasCleared : IDomainEvent
{
public Guid Id { get; set; } = Guid.NewGuid();
public DateTime DateTime { get; set; } = DateTime.UtcNow;
}
From code above seems like the @event object passed to Handle
parameter in my Event Handling class was created from a different MyNamespace.Domain
assembly loaded so it fails because of the type mismatch even though they're technically the same Type definitions, just coming from different loaded assemblies.
This is how I'm printing the number of loaded assemblies:
var domainAssemblyCount= AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.ToUpper().Contains("MYNAMESPACE.DOMAIN")).ToList().Count();
Any ideas??