问题描述
在将某些Windows服务从Framework迁移到.Net Core 3.1时,我们遇到了一些问题。
我们目前最苦苦挣扎的问题是,因为我们实际上有2个级别的依赖注入。一个在Topshelf服务级别,一个在Topshelf.AfterStartingService / Kestrel API级别。
这意味着当我们使用在ServiceA中注册的服务访问ServiceA控制器时,出现错误,表明它无法解析IPackageRegistrationService
我们是否缺少更好的设计模式?有没有一种方法可以将这些依赖项传递到Common.Startup类中,而不必将Common项目中的循环依赖项返回给ServiceA?
错误看起来像
system.invalidOperationException:尝试激活“ ServiceA.Console.Registration.Controllers.PackageRegistrationController”时,无法解析类型为“ ServiceA.Console.Registration.Service.IPackageRegistrationService”的服务。 在Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(类型为serviceType,类型为ImplementationType,CallSiteChain,callSiteChain,ParameterInfo []参数,布尔型throwIfCallSiteNotFound)
ServiceA.Main()像这样注册所有依赖项
private static void Main()
{
DbProviderFactories.RegisterFactory("System.Data.sqlClient",sqlClientFactory.Instance);
var configuration = ConfigurationManager.InitialiseConfigurationBuilder().Build();
var mainRabbitQueue = new MonitoredMessageQueue(new RabbitMessageQueue());
// Dependency Injection
var serviceProvider = new ServiceCollection()
// Services
.AddTransient<IReprocessExecuter,ReprocessExecuter>()
.AddTransient<IFileNameBuilder,FileNameBuilder>()
.AddTransient<IRuleRunner,RuleRunner>()
.AddTransient<ITimeReporter,TimeReporter>()
.AddTransient<IPackageRegistrationService,PackageRegistrationService>()
.AddTransient<NetCore.Console.Common.ServiceHost<BatchProcessorService>,NetCore.Console.Common.ServiceHost<BatchProcessorService>>()
// Console.Common
.AddSingleton<IMessageQueue>(mainRabbitQueue)
.AddSingleton<IMessageQueueStats>(mainRabbitQueue)
.AddSingleton<IServiceLogger>(new ServiceEventLogger(mainRabbitQueue))
.AddTransient<IHeartBeatService,HeartBeatService>()
.AddTransient<IStatusService,StatusService>()
.BuildServiceProvider();
using (var serviceHost = serviceProvider.GetService<NetCore.Console.Common.ServiceHost<BatchProcessorService>>())
{
serviceHost.Run();
}
ServiceCommon.Run()
public void Run()
{
HostFactory.Run(x =>
{
x.Service<TService>(s =>
{
s.AfterStartingService(c =>
{
_healthChecker.Start();
if (!string.IsNullOrEmpty(serviceSettings.ApiUri))
{
Startup.ServiceName = serviceSettings.ServiceName;
Startup.StatusService = _statusService;
_apiService = CreateHostBuilder().Build();
_apiService.Start();
_log.Loginformation($"{serviceSettings.ServiceName} API is running,access health check at {serviceSettings.ApiUri}/healthcheck");
}
});
});
});
}
CreateHostBuilder的外观
public static IHostBuilder CreateHostBuilder(string[] args = null)
{
return Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.UseUrls(ServiceSettings.Default.ApiUri)
.UseSetting(WebHostDefaults.ApplicationKey,Assembly.GetExecutingAssembly().GetName().Name);
});
}
并且StartUp.ConfigureServices看起来像这样
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.Load(ServiceName);
services.AddMvc(options =>
{
options.EnableEndpointRouting = false;
}).AddApplicationPart(assembly)
.AddControllersAsServices();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1",new OpenApiInfo
{
Title = $"{ServiceName}",Version = "v1",Description = $"{ServiceName}"
});
});
var mainRabbitQueue = new MonitoredMessageQueue(new RabbitMessageQueue());
services.AddSingleton<IMessageQueue>(mainRabbitQueue)
.AddSingleton<IMessageQueueStats>(mainRabbitQueue)
.AddTransient<IHeartBeatService,HeartBeatService>()
.AddSingleton<IStatusService>(StatusService);
}
解决方法
由Host.CreateDefaultBuilder
内部s.AfterStartingService(
创建的通用主机具有新的服务提供程序(新的依赖项注入容器)。它没有在Main
中创建的服务提供商的注册。
在.NET Core中,您不需要Topshelf即可将应用程序作为Windows服务运行。借助Microsoft.Extensions.Hosting.WindowsServices
nuget,您可以创建一个通用主机,该主机可以作为Windows服务运行,而Kestrel作为其中一种托管服务。
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseWindowsService()
您可以在主机构建器的Startup
和ConfigureServices
方法中注册类型/服务/依赖项。
关于docs中的UseWindowsService
:
将主机生存期设置为WindowsServiceLifetime,设置内容根目录,并启用使用应用程序名称作为默认源名称的事件日志记录。这是上下文感知的,只有在它检测到该进程正在作为Windows服务运行时才会激活。