NetCore 2.1 Generic Host as a service - c#

I'm trying to build a Windows Service using the latest Dotnet Core 2.1 runtime. I'm NOT hosting any aspnet, I do not want or need it to respond to http requests.
I've followed the code found here in the samples: https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/host/generic-host/samples/2.x/GenericHostSample
I've also read this article: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.1
The code works great when run inside of a console window using dotnet run. I need it to run as a windows service. I know there's the Microsoft.AspNetCore.Hosting.WindowsServices, but that's for the WebHost, not the generic host. We'd use host.RunAsService() to run as a service, but I don't see that existing anywhere.
How do I configure this to run as a service?
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MyNamespace
{
public class Program
{
public static async Task Main(string[] args)
{
try
{
var host = new HostBuilder()
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddJsonFile("hostsettings.json", optional: true);
configHost.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configHost.AddCommandLine(args);
})
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.AddJsonFile("appsettings.json", optional: true);
configApp.AddJsonFile(
$"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",
optional: true);
configApp.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configApp.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<TimedHostedService>();
})
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConsole();
configLogging.AddDebug();
})
.Build();
await host.RunAsync();
}
catch (Exception ex)
{
}
}
}
#region snippet1
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();
}
}
#endregion
}
EDIT: I repeat, this is not to host an ASP.NET Core app. This is a generic hostbuilder, not a WebHostBuilder.

As others have said you simply need to reuse the code that is there for the IWebHost interface here is an example.
public class GenericServiceHost : ServiceBase
{
private IHost _host;
private bool _stopRequestedByWindows;
public GenericServiceHost(IHost host)
{
_host = host ?? throw new ArgumentNullException(nameof(host));
}
protected sealed override void OnStart(string[] args)
{
OnStarting(args);
_host
.Services
.GetRequiredService<IApplicationLifetime>()
.ApplicationStopped
.Register(() =>
{
if (!_stopRequestedByWindows)
{
Stop();
}
});
_host.Start();
OnStarted();
}
protected sealed override void OnStop()
{
_stopRequestedByWindows = true;
OnStopping();
try
{
_host.StopAsync().GetAwaiter().GetResult();
}
finally
{
_host.Dispose();
OnStopped();
}
}
protected virtual void OnStarting(string[] args) { }
protected virtual void OnStarted() { }
protected virtual void OnStopping() { }
protected virtual void OnStopped() { }
}
public static class GenericHostWindowsServiceExtensions
{
public static void RunAsService(this IHost host)
{
var hostService = new GenericServiceHost(host);
ServiceBase.Run(hostService);
}
}

I hope you found the solution for this problem.
In my case I used generic host (introduced in 2.1) for such purpose and then just wrap it up with systemd to run it as a service on Linux host.
I wrote a small article about it https://dejanstojanovic.net/aspnet/2018/june/clean-service-stop-on-linux-with-net-core-21/
I hope this helps

IHostedService if for [asp.net core] backendjob,
if u want to build a windows service on .net core, u should reference this package System.ServiceProcess.ServiceController, and use ServiceBase as base class.
(you can also start from a .net framework windows service and then change the .csproj file)
edit: please see this doc and this code https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNetCore.Hosting.WindowsServices/WebHostWindowsServiceExtensions.cs.
To create a windows service ServiceBase for manage your IHost

Related

Dapr app channel not initialized, make sure -app-port is specified if pubsub subscription is required

I am trying to implement subcriber in C# console app using Dapr, but its giving the below error.
Error:
app channel not initialized, make sure -app-port is specified if
pubsub subscription is required
Exited App successfully
terminated signal received: shutting down Exited Dapr successfully
Code:
public static void Main(string[] args)
{
DaprClient _dapr = new DaprClientBuilder().Build();
_dapr.PublishEventAsync("pubSubName", "topicName", "data");
}
I am able to solve the issue by converting the console app to windows (worker) service.
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyService>();
})
.ConfigureWebHostDefaults((builder) =>
{
builder.UseStartup<Startup>();
});
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDaprClient();
}
public void Configure()
{
}
}
public class MyService: BackgroundService
{
private readonly DaprClient _dapr;
public PaymentService(DaprClient dapr)
{
_dapr = dapr;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _dapr.PublishEventAsync();
await Task.Delay(1000, stoppingToken);
}
}
}

In a .net core 3.0 BackgroundService app, why is my configuration object empty when running as service, but not as console app?

I have a .net Core 3.0 BackgroundService application that works fine when running in console mode, but once i deploy as a service the configuration object that should be loaded from appsettings.json is empty. What gives?
Program.cs
public class Program
{
public static async System.Threading.Tasks.Task Main(string[] args)
{
var hostbuilder = new HostBuilder()
.UseWindowsService()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location))
.AddJsonFile("appsettings.json");
})
.ConfigureLogging(
options => options.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Information))
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Importer>().Configure<EventLogSettings>(config =>
{
config.LogName = "Application";
config.SourceName = "Importer";
});
});
#if (DEBUG)
await hostbuilder.RunConsoleAsync();
#else
await hostbuilder.RunAsServiceAsync();
#endif
}
}
Extension Method for IhostBuilder to run service
public static class ServiceBaseLifetimeHostExtensions
{
public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
}
public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
{
return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
}
}
ServiceBaseLifetime class to handle service lifecycle
public class ServiceBaseLifetime : ServiceBase, IHostLifetime
{
private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();
public ServiceBaseLifetime(IHostApplicationLifetime applicationLifetime)
{
ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
}
private IHostApplicationLifetime ApplicationLifetime { get; }
public Task WaitForStartAsync(CancellationToken cancellationToken)
{
cancellationToken.Register(() => _delayStart.TrySetCanceled());
ApplicationLifetime.ApplicationStopping.Register(Stop);
new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
return _delayStart.Task;
}
private void Run()
{
try
{
Run(this); // This blocks until the service is stopped.
_delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
}
catch (Exception ex)
{
_delayStart.TrySetException(ex);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
Stop();
return Task.CompletedTask;
}
// Called by base.Run when the service is ready to start.
protected override void OnStart(string[] args)
{
_delayStart.TrySetResult(null);
base.OnStart(args);
}
// Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
// That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
protected override void OnStop()
{
ApplicationLifetime.StopApplication();
base.OnStop();
}
}
The actual implementation of the service is irrelevant other than the constructor, which takes the logger and configuration through DI.
private readonly ILogger<Importer> _logger;
private readonly IConfiguration _configuration;
public Importer(IConfiguration configuration, ILogger<Importer> logger)
{
_logger = logger;
_configuration = configuration;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"Why is {_configuration["Key1"]} empty?");
}
appsettings.json
{
"Key1":"some value"
}
When i run through debug the console app starts up and runs and logs and has the configuration loaded from appsettings. When i deploy as a service the configuration object is empty.
Notes: The appsettings file is being read, i can tell this by changing the name of it and it throws an exception for file not found. The appsettings file is also not empty.
My issue appears to be some kind of async race condition problem (I am guessing, not positive). The first tick through ExecuteAsync the configuration is not loaded, but the second time through it is. I had the service dying if it encountered that exception, so i never got it to tick a second time.
This appears to be an XY problem and is worthy of a refactor
Create a strongly typed model to hold the desired settings
public class ImporterSettings {
public string Key1 { get; set; }
}
Refactor hosted service to depend on the settings since tightly coupling services to IConfiguration is a code smell in my opinion
private readonly ILogger<Importer> _logger;
private readonly ImporterSettnigs settings;
public Importer(ImporterSettnigs settings, ILogger<Importer> logger) {
_logger = logger;
this.settings = settings;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
_logger.LogInformation($"This is Key1: {settings.Key1}");
}
Now properly configure start up to use the provided configuration
public class Program {
public static async System.Threading.Tasks.Task Main(string[] args) {
var hostbuilder = new HostBuilder()
.UseWindowsService()
.ConfigureAppConfiguration((hostingContext, config) => {
var path = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
config
.SetBasePath(path)
.AddJsonFile("appsettings.json");
})
.ConfigureLogging(
options => options.AddFilter<EventLogLoggerProvider>(level =>
level >= LogLevel.Information)
)
.ConfigureServices((hostContext, services) => {
//get settings from app configuration.
ImporterSettings settings = hostContext.Configuration.Get<ImporterSettings>();
services
.AddSingleton(settings) //add to service collection
.AddHostedService<Importer>()
.Configure<EventLogSettings>(config => {
config.LogName = "Application";
config.SourceName = "Importer";
});
});
#if (DEBUG)
await hostbuilder.RunConsoleAsync();
#else
await hostbuilder..Build().RunAsync();
#endif
}
}

Performing a health check in .NET Core Worker Service

How can I implement health checks in a .NET Core Worker Service?
The service will be run inside Docker and needs to be able to check the health of the service.
Another way of doing this is to implement IHealthCheckPublisher.
The benefits of this approach is the ability to re-use your existing IHealthChecks or integration with 3rd party libraries that rely on IHealthCheck interface (like this one).
Though you still target Microsoft.NET.Sdk.Web as the SDK you don't need to add any asp.net specifics.
Here is an example:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host
.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services
.AddHealthChecks()
.AddCheck<RedisHealthCheck>("redis_health_check")
.AddCheck<RfaHealthCheck>("rfa_health_check");
services.AddSingleton<IHealthCheckPublisher, HealthCheckPublisher>();
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(5);
options.Period = TimeSpan.FromSeconds(5);
});
});
}
public class HealthCheckPublisher : IHealthCheckPublisher
{
private readonly string _fileName;
private HealthStatus _prevStatus = HealthStatus.Unhealthy;
public HealthCheckPublisher()
{
_fileName = Environment.GetEnvironmentVariable(EnvVariableNames.DOCKER_HEALTHCHECK_FILEPATH) ??
Path.GetTempFileName();
}
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
// AWS will check if the file exists inside of the container with the command
// test -f $DOCKER_HEALTH_CHECK_FILEPATH
var fileExists = _prevStatus == HealthStatus.Healthy;
if (report.Status == HealthStatus.Healthy)
{
if (!fileExists)
{
using var _ = File.Create(_fileName);
}
}
else if (fileExists)
{
File.Delete(_fileName);
}
_prevStatus = report.Status;
return Task.CompletedTask;
}
}
I don't think is worth it to change SDK to Microsoft.NET.Sdk.Web. You will include additional middlewares just because of one health check? No thanks ...
What you could do is to use a different protocol like TCP.
The general idea is:
Create a separate background service that creates a TCP server (take a look at TcpListener.cs)
When you receive a request you have two options: if the application is healthy accept TCP connection otherwise reject it.
If you use containers your orchestrator should have an option to call it over TCP (in k8s there is a property tcpSocket)
If you need more detailed information you may check: Monitoring Health of ASP.NET Core Background Services With TCP Probes on Kubernetes
Cheers!
I think that you should also consider to retain the Microsoft.NET.Sdk.Worker.
Don't change the whole sdk just because of the health checks.
Then you can create a backgroundservice (just like the main worker), in order to update a file to write for example the current timestamp. An example of the background health check worker would be:
public class HealthCheckWorker : BackgroundService
{
private readonly int _intervalSec;
private readonly string _healthCheckFileName;
public HealthCheckWorker(string healthCheckFileName, int intervalSec)
{
this._intervalSec = intervalSec;
this._healthCheckFileName = healthCheckFileName;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
File.WriteAllText(this._healthCheckFileName, DateTime.UtcNow.ToString());
await Task.Delay(this._intervalSec * 1000, stoppingToken);
}
}
}
Then you can add a extension method like this:
public static class HealthCheckWorkerExtensions
{
public static void AddHealthCheck(this IServiceCollection services,
string healthCheckFileName, int intervalSec)
{
services.AddHostedService<HealthCheckWorker>(x => new HealthCheckWorker(healthCheckFileName, intervalSec));
}
}
With this you can add in services the health check support
.ConfigureServices(services =>
{
services.AddHealthCheck("hc.txt", 5);
})
Add HTTPListener and expose the health checks endpoints.
Using HTTPListener does not require adding Microsoft.NET.Sdk.Web SDK.
Program.cs
using Consumer;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
services.AddHostedService<HttpHealthcheck>();
})
.Build();
await host.RunAsync();
HttpHealthcheck.cs
using System.Net;
using System.Text;
namespace Consumer;
public class HttpHealthcheck : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly HttpListener _httpListener;
private readonly IConfiguration _configuration;
public HealthcheckHttpListener(ILogger<Worker> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
_httpListener = new HttpListener();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_httpListener.Prefixes.Add($"http://*:5001/healthz/live/");
_httpListener.Prefixes.Add($"http://*:5001/healthz/ready/");
_httpListener.Start();
_logger.LogInformation($"Healthcheck listening...");
while (!stoppingToken.IsCancellationRequested)
{
HttpListenerContext ctx = null;
try
{
ctx = await _httpListener.GetContextAsync();
}
catch (HttpListenerException ex)
{
if (ex.ErrorCode == 995) return;
}
if (ctx == null) continue;
var response = ctx.Response;
response.ContentType = "text/plain";
response.Headers.Add(HttpResponseHeader.CacheControl, "no-store, no-cache");
response.StatusCode = (int)HttpStatusCode.OK;
var messageBytes = Encoding.UTF8.GetBytes("Healthy");
response.ContentLength64 = messageBytes.Length;
await response.OutputStream.WriteAsync(messageBytes, 0, messageBytes.Length);
response.OutputStream.Close();
response.Close();
}
}
}
What I've done to accomplish this is add Microsoft.NET.Sdk.Web to my Worker, and then configured a web host to run alongside the worker:
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder =>
{
builder.UseStartup<Startup>();
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.AddLogging(builder =>
builder
.AddDebug()
.AddConsole()
);
});
With that done, all that's left to do is map the health check endpoint as you normally would with ASP.NET Core.

Get Instance of HubContext for re-use anywhere At SelfHosted Asp.net core Application

I have got a self hosted asp.net core app working at console. I can send message from my c# windows forms clients. But i want to send message anywhere in my server class to clients. Not one time message. So I need hubcontext instance for re-use it.
I have used IHubContext implement but im getting NullReference exception when I use hub context.
This is my Hub.
public class UniveraHub : Microsoft.AspNetCore.SignalR.Hub
{
public string GetConnectionId()
{
return Context.ConnectionId;
}
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
This is my Startup Class
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseSignalR(routes =>
{
routes.MapHub<UniveraHub>("/UniveraHub");
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddScoped<MyHubHelper>();
}
}
This is my HubHelper Class
public class MyHubHelper
{
private readonly IHubContext<UniveraHub> _hubContext;
public MyHubHelper(IHubContext<UniveraHub> hubContext)
{
_hubContext = hubContext;
}
public MyHubHelper()
{
}
public void SendOutAlert(string msg)
{
_hubContext.Clients.All.SendAsync("ReceivedMessage", msg);
}
}
This program.cs that I'm using my helper class for send message to clients
MyHubHelper helper = new MyHubHelper();
helper.SendOutAlert("Hi from server!");
In Program.cs you can use like below:
public class Program
{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args)
.Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var helper = services.GetRequiredService<MyHubHelper>();
helper.SendOutAlert("Hi from server!");
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}

How to write Web API, Self hosting, Windows Service

I can't make sense of this. I keep getting an exception
Exception thrown: 'System.AggregateException' in mscorlib.dll, with a message: "Internal error in the expression evaluator." and no Inner Exception!
I can't find where the error is coming from other than when the server is being started, this Line: _server.OpenAsync().Wait();
I thought it would be a good idea to use DI and I though my problems were coming from Autofac but as you can see I have remarked it all out and I'm still getting this exception. The pertinent code is in the last method. Please take a look at the code:
using System;
using System.ServiceProcess;
using System.Threading;
using System.Reflection;
using ServicesUtilities;
using System.Web.Http;
using System.Web.Http.SelfHost;
using Autofac;
using Autofac.Integration.WebApi;
namespace SeviceMerge
{
partial class MergeService : ServiceBase, IQuasiServiceBase
{
private HttpSelfHostServer _server;
private bool _runOnStart;
public InjectionService()
{
InitializeComponent();
_runOnStart = Config.Run;
}
protected override void OnStart(string[] args)
{
WebApiListener();
}
protected override void OnStop()
{
_runOnStart = false;
_server.CloseAsync().Wait();
_server.Dispose();
}
void IQuasiServiceBase.OnStart(string[] args, bool isBatchMode)
{
OnStart(args);
}
void IQuasiServiceBase.OnStop()
{
OnStop();
}
bool IQuasiServiceBase.PauseCheck()
{
return false;
}
bool IQuasiServiceBase.StopCheck()
{
return false;
}
private void WebApiListener()
{
var config = new HttpSelfHostConfiguration("http://localhost:26675");
config.Routes.MapHttpRoute(
"Presents",
"api/{controler}/{id}",
new { id = RouteParameter.Optional }
);
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
}
}
Here's my Controller code:
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace AEMtoParmedInject.Controllers
{
public class AemToParmedMergeController : ApiController
{
private ILogger _logger;
private IWorkTheMachine _worker;
public AemToParmedMergeController()
{
_logger = new Logger();
//_worker = worker;
}
[HttpGet]
public HttpResponseMessage Index()
{
return new HttpResponseMessage()
{
Content = new StringContent(
"<html>" +
"<head>" +
"</head>" +
"<body>" +
"<p>By click on the button below you are signaling the AEM to Parmed Merge service to perform it task</p>" +
"<form>" +
"<input type='submit' action='Index' value='Integrate AEM Content'>" +
"</form" +
"</body>" +
"</html>",
Encoding.UTF8,
"text/html"
)
};
}
}
}
Everything in .net core starts with a simple console application and here I give you 5 simple steps that you can use in order to self-host a Web API / Web APP in a worker service and host the final .exe in your windows services, even you can host in Linux Systemd with little changes.
I've created a very simple Worker-Service Web-API template that exists in my GitHub profile. The following steps work 100%, but if you faced any problem, you can ask here or you can clone the template from my GitHub and use it and read the documentation there.
Do the following steps:
Create a .net core console application.
Install packages "Microsoft.AspNetCore.App" and "Microsoft.Extensions.Hosting.WindowsServices" using NuGet.
Create a Worker.cs file which will handle your worker service. put the following codes inside:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace WorkerServiceWebAppTemplate
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
}
Create a Startup.cs file which will handle your web host and create a simple GET API in the root address and responsible to show a simple message. you can extend it. put the following lines of codes in your Startup.cs file:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}
Finally to start worker service and host your Web API inside your worker service, and also in order yo allow your published .exe file to be able to be host in windows services, use the following codes in your Program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<Worker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
}

Categories