Aspnet Zero中使用Windows service (Topshelf)來承載Quartz.net任務
阿新 • • 發佈:2020-08-12
# Aspnet Zero使用Windows service (Topshelf)來承載Quartz.net任務
網上有很多關於如何使用Topshelf建立ABP的Quartz windows服務,但很少(沒有)有介紹如何配合Aspnet Zero使用的文章,本文記錄整合過程,以供參考。
1. 在官方Aspnet Zero模板解決方案中建立Console型別的專案,並安裝以下buget package:
`Topshelf`
`Abp.Quartz`
`Abp.Castle.Log4Net`
`Abp.AspnetCore` (後面有用到)
2. 新增引用`MyCompanyName.AbpZeroTemplate.Core`和`MyCompanyName.AbpZeroTemplate.Core`專案
3. 最終引用如圖:
![](https://img2020.cnblogs.com/blog/28427/202008/28427-20200811115713876-1347830211.png)
4. 建立Module檔案,
- 新增相關DependsOn屬性;
- `abpZeroTemplateEntityFrameworkCoreModule.SkipDbSeed = true;` 這裡我們需要禁用資料庫初始化動作,因為所有的初始化動作都在Host端完成;
- 設定資料庫連線字串`Configuration.DefaultNameOrConnectionString = _appConfiguration.GetConnectionString(
AbpZeroTemplateConsts.ConnectionStringName
);`
最終檔案如下:
```csharp
namespace MyCompanyName.AbpZeroTemplate.WinService
{
[DependsOn(typeof(AbpZeroTemplateCoreModule),
typeof(AbpZeroTemplateEntityFrameworkCoreModule),
typeof(AbpQuartzModule)
)]
public class AbpZeroWinServiceModule : AbpModule
{
private readonly IConfigurationRoot _appConfiguration;
public AbpZeroWinServiceModule(AbpZeroTemplateEntityFrameworkCoreModule abpZeroTemplateEntityFrameworkCoreModule)
{
abpZeroTemplateEntityFrameworkCoreModule.SkipDbSeed = true;
_appConfiguration = AppConfigurations.Get(
typeof(AbpZeroWinServiceModule).GetAssembly().GetDirectoryPathOrNull(),
addUserSecrets: true
);
}
public override void PreInitialize()
{
//Set default connection string
Configuration.DefaultNameOrConnectionString = _appConfiguration.GetConnectionString(
AbpZeroTemplateConsts.ConnectionStringName
);
}
public override void Initialize()
{
this.IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
}
```
5. 修改Program.cs檔案
- 設定工作目錄 `Directory.SetCurrentDirectory(currentDirectory);`, 目的是為了保證工作在windows service時log檔案存放目錄正確,如果不設定, 工作在windows service時log檔案將會存放在system32目錄中
- **註冊IdentityRegistrar以及新增abp依賴**,這一步是必須的,否則會出現依賴錯誤:
`Castle.MicroKernel.Handlers.HandlerException: 'Can't create component 'Portal.Authorization.Users.UserManager' as it has dependencies to be satisfied.`
新增abp依賴的同時我們同時新增log4net和外掛支援.(注意路徑)
```csharp
var services = new ServiceCollection();
IdentityRegistrar.Register(services);
services.AddAbp(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility(
f => f.UseAbpLog4Net().WithConfig(Path.Combine(currentDirectory, $"log4net.config"))
);
options.PlugInSources.AddFolder(Path.Combine(currentDirectory, "Plugins"), SearchOption.AllDirectories);
});
```
完整Program.cs程式碼如下:
```csharp
class Program
{
static void Main(string[] args)
{
var currentDirectory = typeof(Program).GetAssembly().GetDirectoryPathOrNull();
// 設定工作目錄. 保證工作在windows service時log檔案存放目錄正確
// 如果不設定, 工作在windows service時log檔案將會存放在system32目錄中
Directory.SetCurrentDirectory(currentDirectory);
var services = new ServiceCollection();
IdentityRegistrar.Register(services);
services.AddAbp(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility(
f => f.UseAbpLog4Net().WithConfig(Path.Combine(currentDirectory, $"log4net.config"))
);
options.PlugInSources.AddFolder(Path.Combine(currentDirectory, "Plugins"), SearchOption.AllDirectories);
});
HostFactory.Run(x =>
{
x.Service(s =>
{
s.ConstructUsing(name => new AbpZeroWinService());
s.WhenStarted((tc, hostControl) => tc.Start(hostControl));
s.WhenStopped((tc, hostControl) => tc.Stop(hostControl));
});
x.RunAsLocalSystem();
x.StartAutomatically();
x.SetDescription("ABP服務測試");
x.SetDisplayName("ABPTestService");
x.SetServiceName("ABPTestService");
});
}
}
```
6. 啟動ABP. Windows service啟動時會呼叫`s.ConstructUsing(name => new AbpZeroWinService());`, 因此我們在`AbpZeroWinService`中啟動ABP.
- 建立`AbpZeroWinService`類繼承自`ServiceControl`, 在`Start`中初始化`AbpBootstrapper`, 同時在停止`Stop`中銷燬`AbpBootstrapper`. 程式碼如下:
```c#
public class AbpZeroWinService : ServiceControl
{
private AbpBootstrapper _bootstrapper;
public bool Start(HostControl hostControl)
{
_bootstrapper = IocManager.Instance.Resolve();
_bootstrapper.Initialize();
return true;
}
public bool Stop(HostControl hostControl)
{
_bootstrapper.Dispose();
return true;
}
}
```
7. quartz.net的配置`quartz.config`中必須使用`AdoJobStore`型別,並且使用和Host一樣的Quartz資料庫連線,這樣才能實現在host上新增任務,最終由windows service來執行任務。
(**請在HostModule的`PreInitialize`方法中禁用Job執行`Configuration.BackgroundJobs.IsJobExecutionEnabled = false;`**)
8. 執行前確保Quartz.net資料庫已建立,如何建立請參考Quartz.net官方文件
以上如有錯誤的地方請大家指正! 如果更好的實現方式,也請分享一下。
最後給出WindowService專案的原始碼,Aspnet Zero的專案自行解決。
[source code](https://files.cnblogs.com/files/FocusNet/MyCompanyName.AbpZeroTemplate.WinServi