在 C# 中,如何将 T 动态传递给 services.AddHostedService<T>()?
In C#, How can I dynamically pass T into services.AddHostedService<T>()?
我有一个包含多个托管服务的网络服务。
我希望能够通过“appSettings.json”打开和关闭它们。
在“StartUp.cs”中,我填充了一个
的实例
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
是这个枚举:
public enum StateOfUse
{
Inactive,
Active
}
并且每个 属性 对应一个托管服务。
回到 StartUp
class,我添加了以下方法:
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);
}
}
}
这会失败,因为 MethodInfo methodInfo
解析为 null,因此将在下一行抛出异常。方法 IServiceCollection.AddHostedService<THostedService>()
是一个静态方法,包含在 public static class ServiceCollectionHostedServiceExtensions
的 Microsoft.Extensions.DependencyInjection
命名空间中的“Microsoft.Extensions.Hosting.Abstractions”中。 (我不在代码中使用 nameof
因为扩展方法不支持它。:-( )
是否可以通过泛型访问此方法?
如果可以,怎么做?
一个选项是使用 overload of AddHostedService
,它需要一个 implementationFactory
注册每个服务,但如果不活动 returns 一个空实现
services.AddHostedService<ServiceA>( (services => {
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
if(settings.IsServiceAActive())
return new ServiceA();
return NullService();
})
发现我可以通过将扩展方法包装在本地方法中最简单地解决问题,例如:
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>();
或者,我注意到了,在这种情况下,AddHostedService()
的实现细节是已知的,它只是 AddTransient()
的包装器(可以说是“语法糖”),所以我可以通过这样做来避免反射:
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);
}
}
}
我有一个包含多个托管服务的网络服务。
我希望能够通过“appSettings.json”打开和关闭它们。
在“StartUp.cs”中,我填充了一个
的实例 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
是这个枚举:
public enum StateOfUse
{
Inactive,
Active
}
并且每个 属性 对应一个托管服务。
回到 StartUp
class,我添加了以下方法:
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);
}
}
}
这会失败,因为 MethodInfo methodInfo
解析为 null,因此将在下一行抛出异常。方法 IServiceCollection.AddHostedService<THostedService>()
是一个静态方法,包含在 public static class ServiceCollectionHostedServiceExtensions
的 Microsoft.Extensions.DependencyInjection
命名空间中的“Microsoft.Extensions.Hosting.Abstractions”中。 (我不在代码中使用 nameof
因为扩展方法不支持它。:-( )
是否可以通过泛型访问此方法? 如果可以,怎么做?
一个选项是使用 overload of AddHostedService
,它需要一个 implementationFactory
注册每个服务,但如果不活动 returns 一个空实现
services.AddHostedService<ServiceA>( (services => {
HostedServiceSettings settings = Configure<HostedServiceSettings>(services, "HostedServices");
if(settings.IsServiceAActive())
return new ServiceA();
return NullService();
})
发现我可以通过将扩展方法包装在本地方法中最简单地解决问题,例如:
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>();
或者,我注意到了,在这种情况下,AddHostedService()
的实现细节是已知的,它只是 AddTransient()
的包装器(可以说是“语法糖”),所以我可以通过这样做来避免反射:
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);
}
}
}