Hosting Web API in .Net Core Worker Service - cannot reference IWebHostEnvironment - asp.net-core

I'm creating a .NET Core Worker Service, and want to expose ASP.Net Core Web APIs for the service. I'm using .NET Core 3.0.
Initially, my plan was to replace IHostBuilder with IWebHostBuilder and add a Startup class just like a regular Asp.Net Core web app (this is probably an oversimplification, though).
My plan was to simply try to replace
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
with
public static IWebHostBuilder CreateHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
}).UseStartup<Startup>();
}
which may not work at all, but at least it's a starting point...
What's blocking me from trying this approach is that I cannot implement my Startup class because IWebHostEnvironment is unavailable.
Here's my
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UserSecretsId>dotnet-WorkerServices-0E977A2C-F0C8-49E7-B00A-5EB01B99FBEB</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0" />
</ItemGroup>
As far as I know, IWebHostEnvironment should be in the Microsoft.Extensions.Hosting.Abstractions nuget package, but referencing it does not seem to work.
Does anyone have any ideas?
-Thanks!

It is much easier to add a service to an API project. That's just one extra line.
If you can't or don't want to start over, change your project manually. Forget about IWebHostBuilder, it is now obsolete or deprecated or something.
I could post some fragments here but much easier: create a temporary API project, copy over the Program and Startup classes (but keep your namespaces) and then, inside Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<Worker>(); // the worker will run
services.AddControllers();
}

I researched a lot and I did this issue using some new methods which were not well-formed and lack performance, and I tried to find a correct way of doing this problem, finally, I solved it and I've shared my experiences in this post. You can use the following steps which are working 100%, and also, you can clone the Worker-Service Web-API template from my GitHub profile.
Actually, you needn't use IWebHostBuilder. The following steps are enough to self-host a WebAPI in a .net core worker service and host worker service in windows services:
Create a simple console application.
Install packages "Microsoft.AspNetCore.App" and "Microsoft.Extensions.Hosting.WindowsServices" using NuGet in your console application.
Create an empty file named Worker.cs and put the following code inside it:
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);
}
}
}
}
The above class is your worker service main file which is inherited from BackgroundService, personally, I inherit IHostedService most of the time.
Create another file named Startup.cs and put the following lines of codes:
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!");
});
});
}
}
}
The Startup file is your web server file and it starts a host and has a simple GET API (/) returns a simple message, you can extend it.
Finally, you've to start your worker service and your host at the same time. change your Program.cs like the following codes:
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>();
});
}
}
}
The following lines in your Program.cs file do the magic:
.UseWindowsService()
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<Worker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

Related

ASP.NET core start dependency injection at when app is started

I am injecting one of my services as last item in the ConfigureServices method:
services.AddSingleton<IBot>(_ => new De.Impl.Bot(Configuration));
I am running the app in docker container so whenever container is restarted I need to invoke my controller so I can get my service running. How can I get it running in the beginning of the configuration part?
As for .NET Core 3.1 and .NET 5.0
First, you can write your business code as an extension:
using Microsoft.Extensions.Hosting;
public static IHost DoSomething(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var yourService = services.GetService<YourService>();
yourService.DoSomething();
}
return host;
}
So if you want to invoke that method every time your application starts, simply call:
// .NET 5.0 Style
public static void Main(string[] args)
{
CreateHostBuilder(args)
.Build()
.DoSomething()
.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
As for .NET 6.0
It's gonna be easier. Simply access var yourService = app.Services.GetRequiredService<YourService> in the Program.cs.
// .NET 6.0 style, in Program.cs, before app.Run();
var yourService = app.Services.GetRequiredService<YourService>();
yourService.DoSomething();
app.Run();

How do I enable compression for my API on the linux service plan?

I'm deploying a .net-core web API to the Linux app service plan. I haven't created a DOCKERFILE. Just building production release and deploying. Based on the response headers in production, it appears to be using the Kestrel server. I found in the MS documentation that one can enable compression in the code. This approach seems to work perfectly locally, however I do not see the Content-Encoding header on my web API call while in production.
You can add below code in your startup.cs file. It works for me.
// Configure Compression level
services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
// Add Response compression services
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
And you also need add app.UseResponseCompression(); in configure function.
Test Result:
My Startup.cs file.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
namespace corewebapp_linux
{
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)
{
// Configure Compression level
services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
// Add Response compression services
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "corewebapp_linux", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "corewebapp_linux v1"));
}
app.UseResponseCompression();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

'ConfigureServices returning an System.IServiceProvider isn't supported.' in .NET Core 3.1 using Autofac

I want to use autofac injection instead of default .net core solution.
Here is my startup file :
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc(option => option.EnableEndpointRouting = false) ;
var cb = new ContainerBuilder();
cb.RegisterModule<mydependecymodule>();
cb.Populate(services);
var container = cb.Build();
return new AutofacServiceProvider(container);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseMvc(routes=>routes.MapRoute("default","/{controller=home}/{action=index}"));
}
And here is my program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseServiceProviderFactory(new AutofacServiceProviderFactory());
But when I run my application I get this error in my main method:
System.NotSupportedException: 'ConfigureServices returning an System.IServiceProvider isn't supported.'
In ASP.NET Core 3.0 the ASP.NET Core hosting model changed and you can't return an IServiceProvider anymore. This is documented in the Autofac docs for integrating with ASP.NET Core 3.0+.
You have to switch your ConfigureServices to be void, and if you want to register stuff directly with Autofac you need to use ConfigureContainer. You also need to register the AutofacServiceProviderFactory in your Program.Main method when you construct the host. There are examples in the documentation showing how to do this.

Host a SignalR Hub in a .NET Core 3.1 Console

I migrating an old .NET library hosted by an exe from .NET Framework 4.6 to .NET Core 3.1. A part of the assembly is based on a stand alone SignalR hub implemented like this.
//-----------------------------------
// Startup SignalR Server
//-----------------------------------
m_oSignalRServer = WebApp.Start( C_AppSettings.ServerURL );
I understood that the host must be initiated with IHostBuilder and Host.CreateDefaultBuilder but I really don understand how to configure it. And especially, how to I specify the bindings and hub names.
Sample code or books are welcome.
learn.microsoft.com
public static IHostBuilder CreateHostBuilder( string [ ] args ) =>
Host.CreateDefaultBuilder( args ).ConfigureServices( ( hostContext, services ) =>
{
services.AddSignalR( ( hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes( 1 );
} ));
} );
Thanks in advance!
this is what I do and it works fine for me with a .net core 3.1 console app.
open up your .csproj and add the following to it:
<ItemGroup>
<FrameworkReference Include="Microsoft.aspNetCore.App" />
</ItemGroup>
then add the following package via nuget package manager:
Microsoft.AspNetCore.SignalR
this is my basic program.cs:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace GameServer
{
internal class Program
{
private static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
private static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
}
}
and a basic Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace GameServer
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) { Configuration = configuration; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chat");
});
}
}
}
a lot simpler imo hope it helps :)
I trying to explain my problem in more details and hope someone know how to solve the issue.
Microsoft recommend to use Host.CreateDefaultBuilder instead of WebHost.CreateDefaultBuilder as I have understood. Host.CreateDefaultBuilder reads configuration from json files. The problem is that I don't understand how to connect the call services.AddSignalR() to my Hub.
In my old .NET 4.5 version it was easier from my point of view.
The server was started with this code
IDisposable oSignalRServer = WebApp.Start( "http://localhost:3211" );
And the hub was referenced with
ConnectionManager.GetHubContext<C_IOHub>()
Hub definition
[HubName( "IOHub" )]
public class C_IOHub : Hub
But with .NET Core I'm lost how to build this as a standalone server. All examples I have found describe how to attach the Hub to an existing MVC project.
I have a Startup.cs with the following code:
public static void Main( string [ ] args )
{
CreateHostBuilder( args ).Build().Run();
}
public static IHostBuilder CreateHostBuilder( string [ ] args ) =>
Host.CreateDefaultBuilder( args )
.ConfigureServices( ( hostContext, services ) =>
{
services.AddSignalR();
} );
I need the following information
How do I create a standalone Hub in .NET Core?
How do I obtain a reference to the Hub context?
Follows a full example to create and use a hub in the .NET Core 3.1 app:
First read the configuration from appsettings.json
"Azure": {
"SignalR": {
"ClientTimeoutInterval": 3600,
"HandshakeTimeout": 30,
"KeepAliveInterval": 15,
"EnableDetailedErrors": true,
"MaximumReceiveMessageSize": 32000,
"StreamBufferCapacity": 10,
"SupportedProtocols": [ "WebSockets", "ServerSentEvents" ],
"ServerConnectionCount": 1
}
}
Then read the configuration on the startup
private AzureConfiguration azureConfiguration;
Add in to configuration method
services.Configure<AzureConfiguration>(this.Configuration.GetSection(Azure)).AddOptionsSnapshot<Azure>();
Note: you can resolve the configuration like this this.azureConfiguration = provider.GetRequiredService<AzureConfiguration>();.
On the startup, configure method:
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<YourHub>(this.azureConfiguration.SignalR.Endpoint)
});
On the configure services method:
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.ClientTimeoutInterval);
hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.HandshakeTimeout);
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.KeepAliveInterval);
hubOptions.EnableDetailedErrors = this.azureConfiguration.SignalR.EnableDetailedErrors;
hubOptions.MaximumReceiveMessageSize = this.azureConfiguration.SignalR.MaximumReceiveMessageSize;
hubOptions.StreamBufferCapacity = this.azureConfiguration.SignalR.StreamBufferCapacity;
});
So your configuration on the startup is done, now just go create your hub.
After the hub is created, you can inject it using DI in to the controllers, workers, etc... like:
private IHubContext<YourHub, IYourHub> YourHub
{
get
{
return this.serviceProvider.GetRequiredService<IHubContext<YourHub, IYourHub>>();
}
}
PS: You should configure your CORS before adding the hub methods.
services.AddCors(options =>
{
options.AddPolicy(CorsPolicy, builder => builder.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((host) => true));
});

How to start a Asp.Net Core application inside an existing Windows service or Console application

We have an existing windows service that hosts some of our WCF services. These are some of our integration API-s.
What we would like to do is start an Asp.Net Core application in this windows service. This Asp.Net Core application is in separate project and we would like to keep it there. This project would be compiled as ClassLibrary.
This wouldn't be done like in common articles you find on google when you type in "Asp.Net Core as Windows service"...
I found this question but the answer is not suitable. We would like to avoid registering this as a separate service because of the extra work needed in the installation process.
I also thought that if I build the IWebHost and call run on a separate thread that it would work. Indeed the web server starts but all the requests I make to it are invalid as in nothing happens at all.
Has anybody had any experience with this kind of problem?
Class Library project file (targets netstandard2.0)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
</ItemGroup>
</Project>
Service Program.cs (targets net472)
class Program
{
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup(typeof(Startup).Assembly.FullName);
public static void Main(string[] args)
{
Task.Run(() => CreateWebHostBuilder(args).Build().Run());
var rc = HostFactory.Run(x => //1
{
x.Service<TownCrier>(s => //2
{
s.ConstructUsing(name=> new TownCrier()); //3
s.WhenStarted(tc => tc.Start()); //4
s.WhenStopped(tc => tc.Stop()); //5
});
x.RunAsLocalSystem(); //6
x.SetDescription("Sample Topshelf Host"); //7
x.SetDisplayName("Stuff"); //8
x.SetServiceName("Stuff"); //9
}); //10
var exitCode = (int) Convert.ChangeType(rc, rc.GetTypeCode()); //11
Environment.ExitCode = exitCode;
}
}
public class TownCrier
{
readonly Timer _timer;
public TownCrier()
{
_timer = new Timer(1000) {AutoReset = true};
_timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
}
public void Start() { _timer.Start(); }
public void Stop() { _timer.Stop(); }
}
The Service required adding a couple of nuget packages:
Microsoft.AspNetCore
Microsoft.AspNetCore.Hosting.Abstractions
Microsoft.AspNetCore.HostFiltering
Microsoft.AspNetCore.Server.IIS
Microsoft.AspNetCore.Server.IISIntegration
Microsoft.AspNetCore.Server.Kestrel
Microsoft.AspNetCore.Server.Kestrel.Core
Microsoft.Extensions.Configuration.CommandLine
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.UserSecrets
Microsoft.Extensions.Logging.Configuration
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.Debug
Microsoft.Extensions.Logging.EventSource