在我的MVC .NET core 2.2应用程序中,有HostedService负责后台工作。
在Startap类的ConfigureServices方法中注册
services.AddHostedService<Engines.KontolerTimer>();
由于这是独立于用户请求的后台服务,因此我想在应用启动时立即启动我的后台服务。 现在是我的HostedService出现在第一个用户请求之后的情况。
启动MVC Core应用程序时启动HostedService的正确方法是什么
我的秘密看起来像这样一个https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
好像我根本无法启动应用程序。
我的头像CS看起来像
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseSerilog((ctx, config) => { config.ReadFrom.Configuration(ctx.Configuration); })
.UseStartup<Startup>();
}
在第一个用户请求之前,我没有遇到任何中断。 我想念什么吗,这是VS2017创建的默认.Net Core应用程序
这是我的starup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
private Models.Configuration.SerialPortConfiguration serialPortConfiguration;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.Stores.MaxLengthForKeys = 128)
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddDbContext<Data.Parking.parkingContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddHostedService<Engines.KontolerTimer>();}
答案 0 :(得分:2)
使用Visual Studio运行此程序时,可能会使用IIS Express,直到发出第一个请求时,IIS Express才会运行ASP.NET Core项目(这实际上就是IIS默认情况下的工作方式)。这在使用ASP.NET Core 2.2新增的InProcess托管模型时适用,我希望您必须使用它才能看到此问题。有关更多信息,请参见此GitHub issue。
您可以通过从用于承载ASP.NET Core应用程序的.csproj文件中删除AspNetCoreCoreHostingModel XML元素来证明这一理论,该文件将切换回OutOfProcess模式。似乎在VS2017的项目属性对话框中的“调试”下有一个“托管模型”选项,如果您不想直接编辑.csproj,则可以将其更改为“进程外”。
如果只希望对生产站点进行宿主模型的进程外处理,则可以使用Web.config转换。如果您希望它在开发和生产过程中都处于进程外,只需更改我在上面调用的属性就足够了,因为它会自动转换为Web.config属性。如果您希望使用进程内模型,则在IIS应用程序中启用预加载是一个不错的选择(请参见here)。
答案 1 :(得分:1)
背景服务在您的应用程序启动时启动,然后由您来与它同步。
您可以通过使用命名空间BackgroundService
(Microsoft.Extensions.Hosting
程序集)中的Microsoft.Extensions.Hosting.Abstractions
类来实现backhround服务:
首先声明服务的接口(在这种情况下,该接口为空,不是很好,但是很干净):
public interface IMyService : IHostedService
{
}
然后,声明您的服务。以下代码段声明了一项服务,该服务在启动腰部持续5秒钟,然后每2分钟半执行一次任务:
internal sealed class MyService : BackgroundService, IMyService
{
private const int InitialDelay = 5 * 1000; //5 seconds;
private const int Delay = (5 * 60 * 1000) / 2; // 2.5 minutes
private readonly ILogger<MyService> m_Logger;
public MyService(ILogger<MyService> logger, IServiceProvider serviceProvider)
{
if (logger == null)
throw new ArgumentNullException(nameof(logger));
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
this.m_Logger = logger;
this.m_ServiceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
m_Logger.LogDebug($"MyService is starting.");
stoppingToken.Register(() => m_Logger.LogDebug($"MyService background task is stopping because cancelled."));
if (!stoppingToken.IsCancellationRequested)
{
m_Logger.LogDebug($"MyService is waiting to be scheduled.");
await Task.Delay(InitialDelay, stoppingToken);
}
m_Logger.LogDebug($"MyService is working.");
while (!stoppingToken.IsCancellationRequested)
{
await DoSomethingAsync();
await Task.Delay(Delay);
}
m_Logger.LogDebug($"MyService background task is stopping.");
}
catch (Exception ex)
{
m_Logger.LogDebug("MyService encountered a fatal error while w task is stopping: {Exception}.", ex.ToString());
}
}
private async Task DoSomrthingAsync()
{
// do something here
await Task.Delay(1000);
}
}
如您所见,保持后台服务“活动”取决于您。最后,您必须在Startup.cs
方法末尾在ConfigureServices
中注册它:
services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, MyService>();
这足以启动服务。请记住,如果托管在IIS中,则您的应用程序实际上可以在以后启动:每次回收程序集时,(重新)启动应用程序。相反,使用Kestrel提供了一个不会被回收的单实例应用程序。
对于使用.Net Core 2.1或更低版本的用户,Background类不可用,但是您可以从github获得定义(我可以张贴过去使用的内容,因为可以移动github存储库):
//borrowed from .NET Core 2.1 (we are currently targeting 2.0.3)
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken cancellationToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
答案 2 :(得分:0)
托管服务 do 在主机启动时启动。使用WebHost,托管服务will be started right after the application has started。这意味着,如果实施正确,您的托管服务将可以运行而无需请求。
当我在新的ASP.NET Core应用程序上尝试您的示例托管服务时,它可以正常工作,因此,如果对您不起作用,那么显然您的 actual 实现KontolerTimer
不正确。
答案 3 :(得分:0)
如果您希望o服务执行后台任务(类似于旧的Windows服务),建议您使用https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2而不是WebHost。
WebHost添加了很多您可能不需要的东西,因为这似乎是一个简单的后台工作(假设读取您的代码)。
答案 4 :(得分:0)
对我来说...直到第一页请求才开始执行后台任务。
但是随后我在“发布/编辑”中注意到,我没有设置“目标网址”。 (而且我也没有主页索引页面)...
一旦我添加了一个有效的目标网址,该页面就会在发布后弹出,成为我的“第一个”页面请求,并且将启动后台任务。