I'm implementing a framework for C# on ASP.NET using Dotnet 6. I want part of the framework to be extensible by outside parties; they just need to implement a few classes and we can integrate their work via Nuget or direct assembly reference.
Part of what they need to complete is a Registration Class so they can define how their engine should be registered with the dependency injection container. This is an example of what I'd expect 3rd parties to supply:
public class EchoServiceRegistration : IRegisterDI
{
public IServiceCollection Register(IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<EchoEngine>();
return serviceCollection;
}
}
In the consuming application, I'm looking for all of the classes that implement the IRegisterDI interface using the AppDomain class, which is a riff off this SO answer https://stackoverflow.com/a/26750/2573109. (Note: I switched to using name-based matching just for troubleshooting and will change it back to a better implementation once properly solved):
List<Type> allRegistrationClasses = AppDomain
.CurrentDomain
.GetAssemblies()
.SelectMany(it => it.GetTypes())
.Where(tp => tp.GetInterfaces()
.Any(inter => inter.Name == nameof(IRegisterDI)))
.ToList();
As written, this returns 0 types.
On the next troubleshooting iteration, I proved that the registration class is available to the caller. So, I manually created an instance of the EchoServiceRegistration
class, as seen below. This time, the AppDomain contains a single entry for EchoServiceRegistration (as expected).
var allRegistrationClasses = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes()).Where(tp => tp.GetInterfaces().Any(inter => inter.Name == nameof(IRegisterDI))).ToList();
var echoServiceRegistration = new EchoServiceRegistration();
echoServiceRegistration.Register(builder.Services);
if (allRegistrationClasses.Count is not 1) throw new InvalidOperationException(); // Does not throw an exception
To prove I didn't accidentally fix something, I commented out the two lines related to Echo Service Registration, and allRegistrationClasses again contains 0 records.
var allRegistrationClasses = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes()).Where(tp => tp.GetInterfaces().Any(inter => inter.Name == nameof(IRegisterDI))).ToList();
// var echoServiceRegistration = new EchoServiceRegistration();
// echoServiceRegistration.Register(builder.Services);
if (allRegistrationClasses.Count is not 1) throw new InvalidOperationException(); // Throws an exception
My gut reaction is I don't understand how the AppDomain
determines what assemblies to load. I started reading the documentation Microsoft provides about it, but it seems like a deep topic and I won't have a more clear understanding without quite a bit of dedicated reading.
How do I guarantee the full set of classes are available when calling this during the DI Container build? Please let me know if I can provide any additional detail or clarity.