I have a webservice with multiple hosted services.
I want to be able to turn them on and off through "appSettings.json".
In "StartUp.cs", I populate an instance of
public class HostedServiceSettings
{
public StateOfUse BuildMontageTasks { get; init; }
public StateOfUse BuildTasksFromFileCreations { get; init; }
public StateOfUse BusinessEventPuller { get; init; }
public StateOfUse FieldServiceAppointmentChanges { get; init; }
public StateOfUse MvtBriefingCleanupLoop { get; init; }
public StateOfUse SfCacheFileListener { get; init; }
public StateOfUse TaskWorker { get; init; }
}
StateOfUse
is this enum:
public enum StateOfUse
{
Inactive,
Active
}
and each property corresponds to a hosted service.
Back in the StartUp
class, I've added the following method:
private void AddHostedServices(IServiceCollection services)
{
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
foreach (PropertyInfo property in typeof(HostedServiceSettings).GetProperties())
{
StateOfUse stateOfUse = (StateOfUse)property.GetValue(settings);
if (stateOfUse.IsActive())
{
string hostedServiceName = $"PraxedoIntegration.{property.Name}HostedService";
Type hostedServiceType = Type.GetType(hostedServiceName);
Type type = typeof(IServiceCollection);
MethodInfo methodInfo = type.GetMethod("IServiceCollection.AddHostedService");
MethodInfo genericMethod = methodInfo.MakeGenericMethod(hostedServiceType);
genericMethod.Invoke(services, null);
}
}
}
This fails because MethodInfo methodInfo
resolves to null and thus an exception will be thrown in the next line. The method IServiceCollection.AddHostedService<THostedService>()
is a static method inculded in "Microsoft.Extensions.Hosting.Abstractions" in the Microsoft.Extensions.DependencyInjection
namespace in public static class ServiceCollectionHostedServiceExtensions
. (I don't use nameof
in the code because that is not supported for extension methods. :-( )
Is it possible to access this method through generics? If so, how?
CodePudding user response:
One option is to use the overload of AddHostedService
which takes an implementationFactory
registering each service, but if inactive returns a null implementation
services.AddHostedService<ServiceA>( (services => {
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
if(settings.IsServiceAActive())
return new ServiceA();
return NullService();
})
CodePudding user response:
Figured out I could solve the problem most simply by wrapping the extension method in a local method, like:
private void AddHostedServices(IServiceCollection services)
{
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
foreach (PropertyInfo property in typeof(HostedServiceSettings).GetProperties())
{
StateOfUse stateOfUse = (StateOfUse)property.GetValue(settings);
if (stateOfUse.IsActive())
{
string hostedServiceName = $"PraxedoIntegration.{property.Name}HostedService";
Type hostedServiceType = Type.GetType(hostedServiceName);
Type type = typeof(Startup);
MethodInfo methodInfo = type.GetMethod("AddHostedService");
MethodInfo genericMethod = methodInfo.MakeGenericMethod(hostedServiceType);
genericMethod.Invoke(this, new object[] { services });
}
}
}
// Needed as a work-arround because we can't call the extension method with reflection.
public IServiceCollection AddHostedService<THostedService>(IServiceCollection services)
where THostedService : class, IHostedService =>
services.AddHostedService<THostedService>();
Alternative, it's been drawn to my attention, in this instance, the implementation details for AddHostedService()
are known and it is simply a wrapper (arguably "syntactic sugar") for AddTransient()
, so I could avoid the reflection by doing this:
private void AddHostedServices(IServiceCollection services)
{
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
foreach (PropertyInfo property in typeof(HostedServiceSettings).GetProperties())
{
StateOfUse stateOfUse = (StateOfUse)property.GetValue(settings);
if (stateOfUse.IsActive())
{
string hostedServiceName = $"PraxedoIntegration.{property.Name}HostedService";
Type hostedServiceType = Type.GetType(hostedServiceName);
services.AddTransient(typeof(IHostedService), hostedServiceType);
}
}
}