We generally Create Host using the Host.CreateDefaultBuilder()
Method.
The Host.CreateDefaultBuilder returns an IHostBuilder.
The IHostBuilder has some extension methods by which we can configure the builder. After configuring the IHostBuilder We build the IHost by IHostBuilder.Build().
But on .NET Platform Extension 7, a new method is introduced Host.CreateApplicationBuilder().
It gives us an HostApplicationBuilder instance.
It doesn't have extension methods like IHostBuilder to configure, but it has some properties such as Configuration, Environment, Logging, Services, etc.
And using HostApplicationBuilder.Build() we can eventually Build the IHost.
My question is when and why we should build Host using the HostApplicationBuilder instead of IHostBuilder?
And how to configure srvices, configurations, etc on HostApplicationBuilder, do we need to directly use its properties(Configuration, Environment, Logging, Services, etc)?
I tried searching on google but got no answer.
It is documented a bit here and here.
The general idea was to move away from calbacks and move to linear code for configuring everything
Code samples from the link...
Web
var builder = WebApplication.CreateBuilder();
builder.Logging.AddConsole();
builder.Services.AddOptions<MyOptions>().BindConfiguration("MyConfig");
builder.Services.AddHostedService<MyWorker>();
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();
Non-Web
var builder = Host.CreateApplicationBuilder();
builder.Logging.AddConsole();
builder.Services.AddOptions<MyOptions>().BindConfiguration("MyConfig");
builder.Services.AddHostedService<MyWorker>();
var host = builder.Build();
host.Run();
Related
When you create a regular .NET 5 or 6 API project, you get some basic classes such as Program.cs and Startup.cs. I want to replicate that in a class project, because I want to be able to configure my services for dependency injection, but I don't want any controllers or HTTP in my project. As an example, let's assume I want to create a .NET 6 project using minimal API/hosting, and I want to check for file changes in a directory:
Program.cs
static async Task Main(string[] args)
{
await CreateHostBuilder(args).Build().RunAsync();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) => ConfigureServices(services));
static void ConfigureServices(IServiceCollection services)
{
services.AddTranscient<IFileListener, FileListener>();
}
This is probably a good starting point, which is quite similar to Startup.cs in an API project.
Inside my FileListener class, I want to call a method, that listens for file changes in a folder. Let's call it StartListening(). Where would I call that method? At some point I guess I need to do something like:
var fileListenerService = ((IServiceCollection)services).BuildServiceProvider().GetService<IListener>();
await fileListenerService.StartListening();
But where? Inside the Main method? Inside ConfigureServices? Somewhere else?
Maybe I'm looking at this the wrong way, but essentially I just need to call a method and make it run that method until the application is closed.
Microsoft's hosting has a concept of hosted services to handle background tasks, so you can turn your FileListener into hosted service and register it in DI with AddHostedService and the hosting will start it automatically with DI and cancelation signaling support.
Note that consuming scoped services (like EF context with default registration) from the hosted service requires a little bit extra work.
I was trying to migrate the my application from asp.net core 2.1 to 3.0 and there come a first suggested change in program.cs for creation of host.
asp.net core 2.1 program.cs
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
asp.net core 3.0 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>();
});
I can see that in asp.net core 3.0 it is creating Host instead of WebHost and injecting WebHostBuilder while crating Host.
But I don't have an clear idea here what is difference between Host and WebHost and why asp.net core 3.0 application does not allow to create WebHost?
The difference one could see in .NET Core 3.0 vs .NET core 2.2 code is that .NET core 3.0 uses the Generic Host while .NET Core 2.2 use the Web Host for web application.
The Generic host got included with ASP.NET CORE 2.1 and became the de-facto standard for the future version of .NET Core. Though the Generic host got included in .NET core 2.1 it was only used for non HTTP workloads. In.NET Core 3.0 it became a universal standard (HTTP + non HTTP workloads).
The reason for shifting from WebHost builder to more generic Host builder is because the WebHost builder was tied more to HTTP request and works well for Web application but with the advent of Microservices and Docker it felt the need of a more generic Web host so .NET Core team revamped it, making it usable with console application also. With Generic Host it is possible to utilize the logging, configuration, and DI libraries within a console application.
To create a Host we can use the new HostBuilder, which has a similar set of methods and extensions as the existing WebHostBuilder.There is one main difference to be aware of and that is HostBuilder doesn’t provide an extension method that allows you to use a startup class as we can with the WebHostBuilder. This decision was made primarily to avoid the need to create two separate DI containers behind the scenes. With the generic host, a single service collection is configured and then used to build the final service provider.
Reason to use ConfigureWebHostDefaults is that the new host builder is a Generic Host Builder so it is important to tell that we intend to configure the default settings for a Web Host.
Please refer to the Microsoft reference which recommends using Generic Host here
The host is responsible for application startup and lifetime management. The server is responsible for accepting HTTP requests. Part of the host’s responsibility includes ensuring the application’s services and the server are available and properly configured. You can think of the host as being a wrapper around the server. The host is configured to use a particular server; the server is unaware of its host.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
You create a host using an instance of WebHostBuilder. This is typically done in your app’s entry point: public static void Main. A typical Program.cs, shown below, demonstrates how to use a WebHostBuilder to build a host.
I'm setting up a proof of concept featuring two ASP.NET Core applications that are both instrumented with Jaeger to demonstrate how it can propagate a trace between services over the wire. Both applications are being deployed to Azure App Services.
I'm using the OpenTracing Contrib package to automatically inject the Jaeger trace context into my inter-service traffic in the form of HTTP Headers (the package is hardcoded to use that form of transmission). But it appears that those headers are going missing along the way, as the receiving application is unable to resume the tracing context.
Before deploying to Azure, I'm testing the applications locally with Docker Compose, and with that setup the context propagation works fine. It's only once the apps are in Azure that things break.
The applications communicate over HTTPS and I've disabled HSTS and HTTPS redirection in case that might be causing Azure to drop the headers, based on the answer in this previous thread.
I've also tried running both applications in Azure Container Instances, and that seems to be a non-starter - it doesn't fix the context propagation and seems to introduce more bugs around span relationships.
The two applications are nearly identical in their setup, and differ only in the API endpoints they serve.
My CreateWebHostBuild from program.cs:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureServices(services =>
{
// Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions)
services.AddJaeger(CheckoutConfiguration.JaegerSettings.Host);
// Enables OpenTracing instrumentation for ASP.NET Core, CoreFx, EF Core
services.AddOpenTracing();
});
The contents of the AddJaeger extension method which is largely borrowed from the Contrib sample:
public static IServiceCollection AddJaeger(this IServiceCollection services, string jaegerHost = "localhost")
{
if (services == null)
throw new ArgumentNullException(nameof(services));
services.AddSingleton<ITracer>(serviceProvider =>
{
string serviceName = Assembly.GetEntryAssembly().GetName().Name;
ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
ISampler sampler = new ConstSampler(sample: true);
var reporter = new RemoteReporter.Builder()
.WithSender(new UdpSender(jaegerHost, 6831, 0))
.Build();
ITracer tracer = new Tracer.Builder(serviceName)
.WithLoggerFactory(loggerFactory)
.WithReporter(reporter)
.WithSampler(sampler)
.Build();
GlobalTracer.Register(tracer);
return tracer;
});
var jaegerUri = new Uri($"http://{jaegerHost}:14268/api/traces");
// Prevent endless loops when OpenTracing is tracking HTTP requests to Jaeger.
services.Configure<HttpHandlerDiagnosticOptions>(options =>
{
options.IgnorePatterns.Add(request => jaegerUri.IsBaseOf(request.RequestUri));
// We don't need to track Prometheus scraping requests
});
services.Configure<AspNetCoreDiagnosticOptions>(options => {
// We don't need to trace Prometheus scraping requests
options.Hosting.IgnorePatterns.Add(context => context.Request.Path.Equals("/metrics", StringComparison.OrdinalIgnoreCase));
});
return services;
}
My startup.cs configure method to show I'm not doing anything weird with the headers (the metrics extensions are for prometheus-net)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpMetrics();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// Do release exception handling
}
app.UseMetricServer();
app.UseMvc();
}
I expect any calls from one application to the other to propagate the active Jaeger trace context. Instead, the two applications log their traces separately and no link can be discerned between them in the Jaeger UI.
Here's a screenshot of a trace that should have spanned both services, but instead only shows spans from the first service:
Maybe you should check whether the application services which you set up in a hurry are both in the same azure resource group as the VM running the Jaeger all-in-one instance, otherwise the second application might not be able to communicate with the Jaeger instance at all.
I'd like to run the ASP.NET Core web stack along with MVC within a Windows service environment which already hosts an existing application in order to provide a frontend for it. The application uses Autofac for DI concerns which is nice because it already has an extension for Microsoft.Extensions.DependencyInjection (on which ASP.NET Core heavily relies upon).
Now, my problem is: ASP.NET Core wants to register its own services within the IoC container, at a stage where my own container has already been built. The only way I can see to run my own 'application part' along with ASP.NET Core is by setting up my application within the scope of the ASP.NET Core web host Startup class, what appears to make ASP.NET Core behaving like a full blown application framework rather than web framework. What I also tried is dropping the Startup completely and setting the web host up like this:
var containerBuilder = new ContainerBuilder();
var container = containerBuilder.Build();
var webHost = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddMvc();
})
.Configure(app =>
{
app.ApplicationServices = new AutofacServiceProvider(container);
app.UseStaticFiles();
app.UseMvc();
})
.UseKestrel()
.UseIISIntegration()
.Build();
webHost.Run();
However this doesn't work because ASP.NET Core seems to override all configurations as soon as the web host is getting built. So, is there a way to integrate ASP.NET Core as well as MVC in an existing environment rather than the other way around? Maybe by setting it up manually rather than using WebHostBuilder etc.?
The only way I found is using Update() function of Autofac container. But Autofac lib marks this approach as bad practice.
Outside of Startup class:
class Program {
public static IContainer Container { get; private set; }
void Main(){
var builder = new ContainerBuilder();
...
Container = builder.Build();
}
}
And in Startup class:
public abstract class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
// Update existing container
builder.Update(Program.Container);
}
}
For sure its a bad hack + I suggest to avoid this approach.
But the answer
don't build your container before the Startup.Configure
is not always applicable. In my case I used Azure Service Fabric .Net Core WebAPI stateless service and suggestion to build container inside Startup is wrong since I need to inject StatelessService before Startup runs.
I am trying to use C# extension methods and fluent APIs from F# but I can’t figure out how to do it. The following snippet is from Asp.Net Core RC2 as an example:
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
I can’t figure out how to use APIs like this from F#. If someone has experience working with fluent APIs in F#, maybe they can shine some light on it.
The following translation of the C# 'hello world' example works for me:
open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Http
type Startup () =
member this.Configure(app: IApplicationBuilder) =
app.Run(fun context -> context.Response.WriteAsync("Hello world!"))
[<EntryPoint>]
let main argv =
let builder = new WebHostBuilder()
let host = builder.UseKestrel().UseStartup<Startup>().Build()
host.Run()
0
Try to add the full AspNetCore package.
Install-Package Microsoft.AspNetCore
You are most likely missing something.