This issue is related to Job execution not working in dynamically loaded assemblies. My jobs are dlls that gets copied to plugin\Myplugin1 , plugin\MyPlugin2 and so on. Each plugin has an initialize method that adds it's types to DI so all plugins register it's types by themselves. I am using McMaster.NETCore.Plugins nuget to load plugins with it's dependencies properly.
Hangfire server runs as a .Net 6 Console Service. After setting up hangfire, to resolve the jobs in plugin directories I have below simple type resolver
try
{
//first use default resolver. if not try to load from plugin.
var foundType = TypeHelper.DefaultTypeResolver(name);
return foundType;
}
catch (Exception)
{
var asm = Assembly.LoadFrom("[pluginpath]\\Plugin.dll");
return asm.GetType(name)
}
First I statically linked my job dll to hangfire project and set a job which got saved in the database with below information (below is without parameter data).
"{""Type"":""MyPlugin.Messaging.IMessageSender`1[[MyPlugin.Messaging.Email.EMailMessage, MyPlugin.Messaging]], MyPlugin.Messaging"",""Method"":""SendAsync"",""ParameterTypes"":""[\""MyPlugin.Messaging.Email.EMailMessage, MyPlugin.Messaging\""]""
With this serialized information, when Hangfire tries to resolve the types it comes to my custom resolver twice.
- 1st type to resolve: MyPlugin.Messaging.IMessageSender`1[[MyPlugin.Messaging.Email.EMailMessage, MyPlugin.Messaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], MyPlugin.Messaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
- 2nd type to resolve: MyPlugin.Messaging.Email.EMailMessage, MyPlugin.Messaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
In both of the above cases DefaultTypeResolver correctly resolve the types since this dll is directly linked to the server project and job gets executed properly.
Then I removed the direct reference to my plugin and ran the server after placing the plugin.dll in the plugin folder. When I set the task; the same job information is saved to db correctly. Then it hits twice to resolve the types. Now, the DefaultResolver cannot find the type so it goes to the exception where I load the dll from the plugin path and try to return the type.
This is where the problem is which I don't understand. When plugin is directly linked to server project; TypeHelper.DefaultTypeResolver([type name string]); correctly find the type, but when it's not directly linked; Assembly.LoadFrom(...) & GetType([type name string]) returns null.
So, if I list all the types in the loaded assembly it gives all the class names but it doesn't list a type MyPlugin.Messaging.IMessageSender`1[[MyPlugin.Messaging.Email.EMailMessage, MyPlugin.Messaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], MyPlugin.Messaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
However my plugin's initialized code setup it's types in DI as below which seems to be what gets serialized to hangfire database
services.TryAddTransient<IMessageSender<EMailMessage>, EMailMessageSender>();
My confusion is how come above type is resolved by DefaultResolver when the dll is hardwired to server project and fail if loading dynamically? I have made sure that
What may be the problem?