You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Due to the way in which composed assemblies are loaded into a [semi-]isolated AssemblyLoadContext, implementations of services defined in 'common' assemblies are not available to assemblies loaded as part of the compostion.
This issue outlines the cause of this behaviour and considers various ways to resolve this issue. Additional suggestions regarding alternatives are welcomed.
Cause
Due to the requirement of needing to allow additional modules to participate in host composition, assemblies are being loaded at composition, not build time, as shown below:
varbuilder=Host.CreateDefaultBuilder(args).ConfigureHostConfiguration(configurationBuilder =>configurationBuilder.AddCommandLine(args)).UseComposition(config =>config.AddYamlFile(args[0]),"composition");// <- Assemblies loaded here.ConfigureServices(,serviceCollection)=>serviceCollection.AddSingleton<IEventBus,EventBus>())awaitbuilder.Build()// <- Not here.RunAsync();
This results in the AssemblyLoadContext used to isolate module assemblies loading new instances of 'common' assemblies rather than sharing those loaded by the host service (i.e. the IEventBus in the above example).
Suggestion 1
Remove the use of AssemblyLoadContext to ensure all assemblies share a common set of dependencies.
Pros:
Simplifies everything
Cons:
No longer able to load multiple instances/versions of specific assemblies
Suggestion 2
"Touch" common assemblies to ensure they're loaded prior to loading addition module assemblies.
Where using a generic overload of UseComposition would cause the assemblies to be loaded into the host prior to loading the external assemblies.
Pros:
Still pretty simple
Cons:
Ugly and could get extremely onorous
Suggestion 3
Provide a means of interacting more closely with the HostBuilder's Build() process such that external assemblies are loaded only after common assemblies have been loaded/registered with the service container.
A PoC of this approach has been implemented in this branch and seems to be working well. This is achieved by calling ComposableHost.CreateDefaultBuilder instead of Host.CreateDefaultBuilder and works by decorating/wrapping the HostBuilder in a ComposableHostBuilder instance allowing configuration and service registrations to be interrogated prior to loading external assemblies and building the host. An example is shown below:
Removes the need to provide configuration directly to the UseComposition extension method
Allows logging providers/loggers for library debugging to be configured/injected in exactly the same way as logging providers/loggers for application code
Looks more like canonical Microsoft.Extensions code
WRT the second point, the PoC doesn't suffer from the issues outlined in the justification for removing the second DI container (i.e. duplicate singleton services getting created) as it is only used to resolve the module loader instance but neatly causes dependent assemblies to be loaded before this happens. It may be possible to remove the second DI container but with significant increase in complexity (i.e. exacerbating the first point).
Suggestion 4
Allow both the current functionality and the solution provided in #3 via different method overloads.
Pros:
Versatility and speed where required Cons:
Complexity and confusion when reporting/debugging issues
Others?
The text was updated successfully, but these errors were encountered:
Description
Due to the way in which composed assemblies are loaded into a [semi-]isolated
AssemblyLoadContext
, implementations of services defined in 'common' assemblies are not available to assemblies loaded as part of the compostion.This issue outlines the cause of this behaviour and considers various ways to resolve this issue. Additional suggestions regarding alternatives are welcomed.
Cause
Due to the requirement of needing to allow additional modules to participate in host composition, assemblies are being loaded at composition, not build time, as shown below:
This results in the
AssemblyLoadContext
used to isolate module assemblies loading new instances of 'common' assemblies rather than sharing those loaded by the host service (i.e. the IEventBus in the above example).Suggestion 1
Remove the use of AssemblyLoadContext to ensure all assemblies share a common set of dependencies.
Pros:
Cons:
Suggestion 2
"Touch" common assemblies to ensure they're loaded prior to loading addition module assemblies.
Something like this:
Where using a generic overload of UseComposition would cause the assemblies to be loaded into the host prior to loading the external assemblies.
Pros:
Cons:
Suggestion 3
Provide a means of interacting more closely with the HostBuilder's
Build()
process such that external assemblies are loaded only after common assemblies have been loaded/registered with the service container.A PoC of this approach has been implemented in this branch and seems to be working well. This is achieved by calling
ComposableHost.CreateDefaultBuilder
instead ofHost.CreateDefaultBuilder
and works by decorating/wrapping theHostBuilder
in aComposableHostBuilder
instance allowing configuration and service registrations to be interrogated prior to loading external assemblies and building the host. An example is shown below:Pros:
Microsoft.Extensions
codeCons:
WRT the second point, the PoC doesn't suffer from the issues outlined in the justification for removing the second DI container (i.e. duplicate singleton services getting created) as it is only used to resolve the module loader instance but neatly causes dependent assemblies to be loaded before this happens. It may be possible to remove the second DI container but with significant increase in complexity (i.e. exacerbating the first point).
Suggestion 4
Allow both the current functionality and the solution provided in #3 via different method overloads.
Pros:
Cons:
Others?
The text was updated successfully, but these errors were encountered: