J'ai implémenté IHostedService dans un site Web principal asp.net. Cela fonctionne très bien, mais le problème est que je veux qu'il soit démarré lorsque le serveur d'hébergement démarre ou que le service IIS redémarre, mais il ne le sera pas sauf si la première demande au site Web arrive.
MISE À JOUR 1: "Module d'initialisation d'application", suggéré par @Arthur n'a pas aidé. Ni sur site ni au niveau du serveur.
La configuration que j'ai utilisée:
<applicationInitialization
doAppInitAfterRestart="true"
skipManagedModules="false"
remapManagedRequestsTo="init.htm">
<add initializationPage="/init.htm" hostName="localhost"/>
</applicationInitialization>
PDATE 2: Voici comment j'ai implémenté l'interface
internal class PaymentQueueService : IHostedService, IDisposable
{
private readonly ILogger _logService;
private Timer _timerEnqueue;
public PaymentQueueService(ILogger logService)
{
_logService = logService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logService.LogInformation("Starting processing payments.");
_timerEnqueue = new Timer(EnqueuePayments, null, TimeSpan.Zero,
TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
private void EnqueuePayments(object state)
{
_logService.LogInformation("Enqueueing Payments.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logService.LogInformation("Stopping processing payments.");
_timerEnqueue?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timerEnqueue?.Dispose();
}
}
La classe Program dans le fichier main.cs:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).ConfigureServices(services =>
{
services.AddHostedService<PaymentQueueService>();
}).Configure((IApplicationBuilder app) =>
{
app.UseMvc();
})
.UseStartup<Startup>();
}
La classe de démarrage:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
}
}
Étant donné que le "module d'initialisation d'application" suggéré ne fonctionnait pas, vous pourriez envisager de passer l'appel vous-même avec un client, car
Le module démarre le processus pour l'application ASP.NET Core lorsque la première demande arrive et redémarre l'application si elle s'arrête ou se bloque.
public class Program {
static Lazy<HttpClient> client = new Lazy<HttpClient>();
public static async Task Main(string[] args) {
var Host = CreateWebHostBuilder(args).Start();//non blocking start
using (Host) {
bool started = false;
do {
var response = await client.Value.GetAsync("site root");
started = response.IsSuccessStatusCode;
await Task.Delay(someDelayHere);
} while (!started);
Host.WaitForShutdown();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(services => {
services.AddHostedService<PaymentQueueService>();
})
.Configure((IApplicationBuilder app) => {
app.UseMvc();
})
.UseStartup<Startup>();
}
REMARQUE:
Pour empêcher les applications hébergées hors processus de se terminer, utilisez l'une des approches suivantes:
- Envoyez une requête ping à l'application depuis un service externe afin de la faire fonctionner.
- Si l'application héberge uniquement des services d'arrière-plan, évitez IIS l'hébergement et utilisez un service Windows pour héberger l'application ASP.NET Core.
Voici comment je crée mon webservices
. Dans le Startup.cs
J'injecte mon IHostedServices
. Container
est une classe de StructureMap
. Je l'ai copié à partir d'un projet existant, il ne correspond donc pas à 100% à votre échantillon.
public class Program
{
public static void Main(string[] args)
{
Config.Directories.EnsureDirectoryTree();
var isService = !(Debugger.IsAttached || args.Contains("--console"));
var webHostService = MyWebHostService.BuildWebHostService(args.Where(arg => arg != "--console").ToArray());
if (isService)
{
ServiceBase.Run(webHostService);
}
else
{
webHostService.InitializeBackend();
webHostService.Host.Run();
}
}
}
public class MyWebHostService : WebHostService
{
public static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public IWebHost Host { get; }
public static ZlsWebHostService BuildWebHostService(string[] args)
{
ConfigureNLog();
Logger.Info("{0} (v{1}) starting...", Config.ServiceName, GetApplicationVersion());
// restore config files
Config.Files.EnsureRestored();
var Host = CreateWebHostBuilder(args).Build();
return new ZlsWebHostService(Host);
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var pathToExe = Assembly.GetExecutingAssembly().Location;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
return WebHost.CreateDefaultBuilder()
.UseKestrel()
.UseContentRoot(pathToContentRoot)
.ConfigureAppConfiguration((context, config) =>
{
config.SetBasePath(Config.Directories.ActiveConfig);
config.AddJsonFile(Config.Files.KestrelConfig.RelativePath, true, true);})
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
if(hostingContext.HostingEnvironment.IsDevelopment())
logging.AddDebug();
})
.UseNLog()
.UseStartup<Startup>();
}
public MyWebHostService(IWebHost Host) : base(Host)
{
this.Host = Host;
}
protected override void OnStarting(string[] args)
{
InitializeBackend();
base.OnStarting(args);
}
protected override void OnStarted()
{
Logger.Info("{0} started...", Config.ServiceName);
base.OnStarted();
}
protected override void OnStopped()
{
Logger.Info("{0} stopped...", Config.ServiceName);
base.OnStopped();
}
...
}
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.Microsoft.com/fwlink/?LinkID=398940
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<PaymentQueueService>();
...
var container = new Container(c =>
{
c.IncludeRegistry<MyFooRegistry>();
c.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
...
}
L'héberger "OutOfProcess" fait l'affaire, mais gardez à l'esprit que ce n'est pas la manière préférée. J'ai créé un dépôt sur GitHub avec le code donné et vous pouvez vérifier la modification lors du dernier commit.
Source: https://github.com/tvdias/SO58831661
Pour une explication approfondie sur les différences entre InProcess et OutOfProcess, je recommande de lire cette page: https://weblog.west-wind.com/posts/2019/Mar/16/ASPNET-Core-Hosting-on-IIS -avec-ASPNET-Core-22 .