Как запустить HostedService в приложении MVC Core без http-запроса - PullRequest
0 голосов
/ 16 января 2019

В моем приложении 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>();}

Ответы [ 4 ]

0 голосов
/ 25 января 2019

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

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

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

0 голосов
/ 24 января 2019

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

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

0 голосов
/ 25 января 2019

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

Вы можете реализовать службу backhround, используя класс BackgroundService из пространства имен Microsoft.Extensions.Hosting (Microsoft.Extensions.Hosting.Abstractions сборка):

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

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();
    }
}
0 голосов
/ 16 января 2019

Хостинг-сервисы do запускаются при запуске хоста. В WebHost размещенные службы будут запущены сразу после запуска приложения . Это означает, что при правильной реализации размещенная служба будет работать без запроса на вход.

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

...