Как запустить HostedService в приложении MVC Core без HTTP-запроса

В моем приложении MVC .NET core 2.2 есть HostedService, который выполняет фоновую работу.

Это зарегистрировано в методе ConfigureServices класса Startap.

services.AddHostedService<Engines.KontolerTimer>();

Поскольку это фоновая служба, независимая от запросов пользователей, я хочу запустить фоновую службу сразу после запуска приложения. Теперь мой HostedService запускается после первого запроса пользователя.

Как правильно запустить HostedService при запуске приложения MVC Core

Моя служба выглядит так: 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>();
    }

И я не попадаю ни в какую точку останова перед первым запросом пользователя. Я что-то скучаю, это приложение .Net Core по умолчанию, созданное VS2017

Вот мой 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>();}

person adopilot    schedule 16.01.2019    source источник
comment
Как вы его устроили? У меня возникли проблемы с развертыванием HostedService в IIS   -  person Adrita Sharma    schedule 14.08.2019
comment
@AdritaSharma, чтобы приложение запускалось автоматически в IIS, необходимо использовать инициализацию приложения. stackoverflow.com/questions/46551660/   -  person Eric    schedule 11.09.2020
comment
Убив несколько дней с той же проблемой в IIS v10 и ASP.NET Core 5, я нашел решение. В дополнение к инициализации приложения вы должны установить для пула приложений значение Startmode=AlwaysRunning, а для сайта - Preload Enabled=true (оба значения можно найти в расширенных настройках). См. Также stackoverflow.com/a/46573873/1443733   -  person Soko    schedule 07.04.2021


Ответы (5)


Когда вы запускаете это с помощью Visual Studio, вы, вероятно, используете IIS Express, который не будет запускать ваш проект ASP.NET Core до тех пор, пока не будет сделан первый запрос (это действительно так, как IIS работает по умолчанию). Это применимо при использовании новой модели хостинга InProcess в ASP.NET Core 2.2, которую, как я полагаю, вы должны использовать, чтобы увидеть эту проблему. Дополнительную информацию см. В этой проблеме GitHub.

Вы можете доказать эту теорию, удалив XML-элемент AspNetCoreHostingModel из файла .csproj, который вы используете для размещения приложения ASP.NET Core (которое вернет его в режим OutOfProcess). Похоже, есть опция «Модель хостинга» в разделе «Отладка» в диалоговом окне свойств проекта VS2017, которую вы можете изменить на «Вне обработки», если вы не хотите напрямую редактировать .csproj.

Если вы хотите, чтобы модель хостинга была вне процесса только для производственного сайта, вы можете, например, использовать преобразование Web.config. Если вы хотите, чтобы он был вне процесса как во время разработки, так и в процессе производства, достаточно просто изменить свойство, которое я назвал выше, поскольку оно автоматически преобразуется в свойство Web.config. Если вы предпочитаете использовать внутрипроцессную модель, включение предварительной загрузки в приложении IIS - хороший вариант (описано в здесь).

person Kirk Larkin    schedule 25.01.2019
comment
Использование Out of Process работает при запуске из Visual Studio, но ничего не делает при размещении в IIS. Для IIS необходимо использовать инициализацию приложения. stackoverflow.com/questions/46551660/ - person Eric; 11.09.2020

Фоновые службы запускаются при запуске вашего приложения, и вам решать, как с ним синхронизироваться.

Вы можете реализовать фоновую службу, используя класс BackgroundService из пространства имен _2 _ (_ 3_ сборка):

Сначала объявите интерфейс вашей службы (в данном случае он пустой, не красивый, но чистый):

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 DoSomethingAsync()
    {
         // 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();
    }
}
person Yennefer    schedule 25.01.2019
comment
Как вы его устроили? У меня возникли проблемы с развертыванием HostedService в IIS - person Adrita Sharma; 14.08.2019
comment
Я использовал пустельгу и ngnix на Linux. Я не пробовал использовать IIS. IMHO Обратите внимание, что такие задачи лучше подходят, когда служба не перезагружается: когда происходит перезапуск домена приложения (например, в IIS), вы в конечном итоге активируете эти события чаще, чем обычно, и они могут иметь непредвиденные последствия. - person Yennefer; 17.08.2019

Для меня ... фоновые задачи не запускались до запроса первой страницы.

Но затем я заметил в моей публикации / редактировании, что у меня не установлен целевой URL. (а также у меня не было домашней индексной страницы) ...

Как только я добавлю действительный целевой URL ... эта страница появится после публикации и станет моим первым запросом страницы, и начнутся фоновые задачи.

person Brian Rice    schedule 24.09.2020

Если вы хотите, чтобы служба выполняла фоновые задачи (похожие на старые службы Windows), я бы посоветовал вам использовать: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2 вместо WebHost.

WebHost добавляет много вещей, которые, вероятно, вам не понадобятся, поскольку кажется простой фоновой работой (при условии, что вы читаете ваш код).

person gatsby    schedule 24.01.2019

Размещенные службы запускаются при запуске хоста. С WebHost размещенные службы будет запущен сразу после запуска приложения. Это означает, что при правильной реализации ваша размещенная служба будет работать без запроса.

Когда я пробую ваш пример размещенной службы в новом приложении ASP.NET Core, он работает нормально, поэтому, если он не работает для вас, то, очевидно, ваша фактическая реализация KontolerTimer неверна.

person poke    schedule 16.01.2019
comment
Странно то, что я не попадаю в точку останова в первой строке ConfigureServices, пока не сделаю первый запрос. В Visual Studio я отключил стартовую веб-страницу в свойствах, но то же самое происходит, когда я развертываю свое приложение в IIS. Нет вызова методов StarUp до тех пор, пока не будет сделан первый HTTP-запрос. - person adopilot; 16.01.2019
comment
Да, они определенно этого не делают при использовании iis express. Может напрямую измениться поведение в iis или kestral. - person Sam; 03.06.2019
comment
Как вы его устроили? У меня возникли проблемы с развертыванием HostedService в IIS - person Adrita Sharma; 14.08.2019