将 Windows 服务与 aspnetboilerplate 集成

Integrate a Windows service with aspnetboilerplate

目标

好的,所以我的目标是在我的 aspnetboilerplate Web 应用程序旁边有一个 windows 服务 运行。 windows 服务的原因是它将 运行 来自完全独立的虚拟服务器的 Hangfire 后台作业。

配置

这是我用来让事情顺利进行的模板

仅供参考,一切都在网络应用程序中运行。

这是服务程序文件(我正在使用 Topshelf,但我认为这不重要)。

HostFactory.Run(x =>
{
    x.Service<PortalService>(sc =>
    {
        sc.ConstructUsing(() => new PortalService());

        sc.WhenStarted((tc, hostControl) => tc.Start(hostControl));
        sc.WhenStopped((tc, hostControl) => tc.Stop(hostControl));
    });

    x.UseLog4Net();
    x.RunAsLocalSystem();
    x.SetServiceName(PortalService.ServiceName);
    x.SetDisplayName(PortalService.ServiceDisplayName);
    x.SetDescription(PortalService.ServiceDescription);
    x.StartAutomatically();
});

这是服务模块

[DependsOn(typeof(PortalCoreModule), typeof(PortalEntityFrameworkModule), typeof(PortalApplicationModule), typeof(AbpHangfireModule))]
public class PortalServiceModule : AbpModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(typeof(PortalServiceModule).GetAssembly());
    }

    public override void PreInitialize()
    {
        Configuration.BackgroundJobs.UseHangfire(config =>
        {
            config.GlobalConfiguration.UseSqlServerStorage(PortalConsts.ConnectionStringName, new SqlServerStorageOptions
            {
                PrepareSchemaIfNecessary = true
            });

            config.Server = new BackgroundJobServer(new BackgroundJobServerOptions
            {
                Queues = new[] {BackgroundJobQueueNames.Critical, BackgroundJobQueueNames.Autotask, BackgroundJobQueueNames.Default}
            });

            config.GlobalConfiguration.UseConsole();
        });

        GlobalJobFilters.Filters.Add(new DisableMultipleQueuedItemsFilter());
    }
}

然后这是我的服务代码

public class PortalService : ServiceControl
{
    public const string ServiceName = "PortalWorker";
    public const string ServiceDisplayName = "Portal Worker";
    public const string ServiceDescription = "Process the background jobs for the Portal.";

    private AbpBootstrapper _bootstrapper;

    public bool Start(HostControl hostControl)
    {
        _bootstrapper = AbpBootstrapper.Create<PortalServiceModule>();

        _bootstrapper.IocManager
            .IocContainer
            .AddFacility<LoggingFacility>(f => f.UseAbpLog4Net().WithConfig("log4net.config"));

        _bootstrapper.Initialize();
        return true;
    }

    public bool Stop(HostControl hostControl)
    {
        _bootstrapper.Dispose();
        return true;
    }
}

然而每次我 运行 服务时,我都会收到以下异常

Castle.MicroKernel.ComponentNotFoundException: 'No component for supporting the service Portal.BackupMonitoring.IBackupMonitoringManager was found'

但是,如果我要 运行 Web 应用程序,它工作正常,所以对我来说这不是 IBackupMonitoringManager 的问题。一定是Service不能注册DI的问题。

我查看了这个 repo 中的示例 https://github.com/aspnetboilerplate/aspnetboilerplate-samples

但是这些示例要么已经过时,要么我看不到带有 .net 核心 Web 应用程序的控制台应用程序示例。

如能提供有关从何处着手的任何帮助,我们将不胜感激。

更新

这是作业尝试执行时的新异常。

Castle.MicroKernel.Handlers.HandlerException: 'Can't create component 'Portal.Authorization.Users.UserManager' as it has dependencies to be satisfied.

'Portal.Authorization.Users.UserManager' is waiting for the following dependencies: - Service 'Portal.Authorization.Roles.RoleManager' which was registered but is also waiting for dependencies. 'Portal.Authorization.Roles.RoleManager' is waiting for the following dependencies: - Service 'System.Collections.Generic.IEnumerable1[[Microsoft.AspNetCore.Identity.IRoleValidator1[[Portal.Authorization.Roles.Role, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered. - Service 'Microsoft.AspNetCore.Identity.ILookupNormalizer' which was not registered. - Service 'Microsoft.AspNetCore.Identity.IdentityErrorDescriber' which was not registered. - Service 'Microsoft.Extensions.Logging.ILogger1[[Abp.Authorization.Roles.AbpRoleManager2[[Portal.Authorization.Roles.Role, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Portal.Authorization.Users.User, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Abp.ZeroCore, Version=3.2.4.0, Culture=neutral, PublicKeyToken=null]]' which was not registered. - Service 'Microsoft.Extensions.Options.IOptions1[[Microsoft.AspNetCore.Identity.IdentityOptions, Microsoft.Extensions.Identity.Core, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered. - Service 'Microsoft.AspNetCore.Identity.IPasswordHasher1[[Portal.Authorization.Users.User, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered. - Service 'System.Collections.Generic.IEnumerable1[[Microsoft.AspNetCore.Identity.IUserValidator1[[Portal.Authorization.Users.User, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered. - Service 'System.Collections.Generic.IEnumerable1[[Microsoft.AspNetCore.Identity.IPasswordValidator1[[Portal.Authorization.Users.User, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered. - Service 'Microsoft.AspNetCore.Identity.ILookupNormalizer' which was not registered. - Service 'Microsoft.AspNetCore.Identity.IdentityErrorDescriber' which was not registered. - Service 'System.IServiceProvider' which was not registered. - Service 'Microsoft.Extensions.Logging.ILogger1[[Microsoft.AspNetCore.Identity.UserManager1[[Portal.Authorization.Users.User, Portal.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Extensions.Identity.Core, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]' which was not registered. '

我想我现在已经明白了,所以我打算 post 它作为答案,但如果有任何问题请告诉我。

首先我必须安装 Abp.AspNetCore 包。这允许访问 IServiceCollection.AddAbp() 扩展方法。

所以我的Program.cs变成了这个

static void Main(string[] args)
{
    Clock.Provider = ClockProviders.Utc;

    var services = new ServiceCollection();
    IdentityRegistrar.Register(services);

    services.AddAbp<PortalServiceModule>(options =>
    {
        //Configure Log4Net logging
        options.IocManager.IocContainer.AddFacility<LoggingFacility>(
            f => f.UseAbpLog4Net().WithConfig("log4net.config")
        );
    });

    HostFactory.Run(x =>
    {
        x.Service<PortalService>(sc =>
        {
            sc.ConstructUsing(() => new PortalService());

            sc.WhenStarted((tc, hostControl) => tc.Start(hostControl));
            sc.WhenStopped((tc, hostControl) => tc.Stop(hostControl));
        });

        x.UseLog4Net();
        x.RunAsLocalSystem();
        x.SetServiceName(PortalService.ServiceName);
        x.SetDisplayName(PortalService.ServiceDisplayName);
        x.SetDescription(PortalService.ServiceDescription);
        x.StartAutomatically();
    });
}

我在使用 PortalServiceModule 时遇到了一些问题。自动映射器配置已经在 PortalApplicationModule 中,因此我可能需要重新访问它以删除重复代码。目前,这就是我的工作。

[DependsOn(typeof(PortalCoreModule), typeof(PortalEntityFrameworkModule), typeof(PortalApplicationModule), typeof(AbpHangfireModule), typeof(AbpZeroCoreModule), typeof(AbpAutoMapperModule))]
public class PortalServiceModule : AbpModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(typeof(PortalServiceModule).GetAssembly());

        Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg =>
        {
            cfg.CreateMap<object, int?>().ConvertUsing<NullableIntTypeConverter>();

            // Scan the assembly for classes which inherit from AutoMapper.Profile
            cfg.AddProfiles(typeof(PortalApplicationModule).GetAssembly());
        });

        Configuration.BackgroundJobs.UseHangfire(config =>
        {
            config.GlobalConfiguration.UseSqlServerStorage(PortalConsts.ConnectionStringName, new SqlServerStorageOptions
            {
                PrepareSchemaIfNecessary = true
            });

            config.Server = new BackgroundJobServer(new BackgroundJobServerOptions
            {
                Queues = new[] { BackgroundJobQueueNames.Critical, BackgroundJobQueueNames.Autotask, BackgroundJobQueueNames.Default }
            });

            config.GlobalConfiguration.UseConsole();
        });

        GlobalJobFilters.Filters.Add(new DisableMultipleQueuedItemsFilter());
    }

    public override void PostInitialize()
    {
        HangfireJobsConfigurer.Configure();
    }
}

然后我的实际服务代码变成了这个

public class PortalService : ServiceControl
{
    public const string ServiceName = "PortalWorker";
    public const string ServiceDisplayName = "Portal Worker";
    public const string ServiceDescription = "Process the background jobs for the Portal.";

    private AbpBootstrapper _bootstrapper;

    public bool Start(HostControl hostControl)
    {
        _bootstrapper = AbpBootstrapper.Create<PortalServiceModule>();

        _bootstrapper.Initialize();

        return true;
    }

    public bool Stop(HostControl hostControl)
    {
        _bootstrapper.Dispose();
        return true;
    }
}

现在我可以 运行 来自 windows 服务的后台作业,尽管我会尝试减少依赖性和重复代码。