Context: Simple out-of-the-box blazor 3.1 SERVER SIDE example - adding logging (just trying to get stuff to the debug conolse in VS2019 in components).
I leave ConfigureServices as is, and have found SO examples that logging is setup in program.cs(!)
This works:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(
logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
ect.
In my components, everything is resolved fine with
[Inject] public ILogger<Foo> Logger { get; set; }
But why not in ConfigureServices, where rest of stuff, that should be DI'ed, is setup ???
Is there a "under the hood" DI going on? (a little like what was
going on in .Net frameework w/ webapi)?
Can it be moved to ConfigureServices with excact same result (or
at all )?
In ASP.NET core 2.x you could partially configure your dependency injection container in Program.cs, and inject the configured classes into Startup.cs. So the ConfigureLogging() method calls ConfigureServices() on the IWebHostBuilder instance, and configures some settings. As these services are configured in the DI container before Startup is instantiated, they can be injected into the Startup constructor:
public Startup(
IConfiguration configuration,
ILogger<Startup> logger) // Inject pre-configured service
{
}
More
But this approach is no longer supported in ASP.NET Core 3.0! The problem is that it requires building the dependency injection container twice.
Is there a "under the hood" DI going on? (a little like what was going
on in .Net frameework w/ webapi)?
The ConfigureLogging just call ConfigureServices without building of service provider
public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ILoggingBuilder> configureLogging)
{
return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder)));
}
Can it be moved to ConfigureServices with excact same result (or at all )?
yes, you can move it to ConfigureServices and the result will be the same
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddConsole();
});
}
So, where to add logging configuration is up to you. Logging is infrastructure and can be added as a configuration of host to Program.cs. Your business classes should be added to DI in Startup.
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.
The Configure method is working like magic in ASP.NET Core 3.1.
Scenario 1
When a new project is created, the framework scaffolds the following method signature:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
No surprises that the application works with this signature, because I can presume that the ASP.NET Core framework expects the signature to be as is.
Scenario 2
The second parameter IWebHostEnvironment is removed:
public void Configure(IApplicationBuilder app)
Application works.
Scenario 3
Injected my DbContext added to IServiceCollection in 'ConfigureServices' method along with logger:
public void Configure(IApplicationBuilder app, ILogger<Startup> logger, VegaDbContext vegaDbContext)
Surprisingly, application works. Looks like the framework is capable enough to resolve types added to service collection. Good sign.
Inline is implementation of ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<VegaDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("VegaDb")));
services.AddControllers();
}
Scenario 4
Injected the WeatherForecastController, which I presume gets added to IServiceCollection via services.AddControllers():
public void Configure(IApplicationBuilder app, WeatherForecastController weatherForecastController)
Application does not work. The following exception is thrown:
System.Exception: 'Could not resolve a service of type
'Vega.Controllers.WeatherForecastController' for the
parameter 'weatherForecastController' of method 'Configure' on type
'Vega.Startup'.'
Can some one explain how the method invocation is actually done by the framework and how it is capable of resolving few types like the ILogger and VegaDbContext but not the WeatherForecastController.
It works by using the dependency injection infrastructure. The arguments to Configure are retrieved from the web host's ServiceProvider. The keyword here is "service"--by default controllers are not added as services to the service collection.
In order to access controllers via dependency injection you need to call the AddControllersAsServices extension method for IMvcCoreBuilder or IMvcBuilder in your ConfigureServices method.
services.AddControllers()
.AddControllersAsServices();
// or
services.AddControllersWithViews()
.AddControllersAsServices();
// or
services.AddMvc()
.AddControllersAsServices();
I have a .Net core (v2.2) API. That API uses some external service calls. We want to make them as fire and forget calls. We used .net core Background service to implement that. We have multiple background services. Now if I register all those IHostedService in Dependency Injection, The last registered background service works, and others come as null in constructor injection of project. Registering dependencies like:
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
And also, I tried adding them as:
services.AddSingleton<IHostedService, BackgroundServiceA>();
services.AddSingleton<IHostedService, BackgroundServiceB>();
In both the cases, only BackgroundServiceB works, BackgroundServiceA comes as null in constructor injection.
To handle this, I was using ServiceProvider to get the object.
var services = serviceProvider.GetServices<IHostedService>();
_backgroundServiceA = services.First(o => o.GetType() == typeof(BackgroundServiceA)) as BackgroundServiceA;
Is it a good way to handle such an issue or I am missing something while registering dependencies.
Also, can we trigger both the background calls parallel?
(edit, I initially answered the wrong question....)
Dependency injection doesn't work with multiple implementations of the same interface. Since each hosted service is registered as an implementation of IHostedService, only one service can be easily injected into other types.
However you can register each service again, with a factory method to locate the background singleton instance;
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
services.AddSingleton<BackgroundServiceA>(p => p.GetServices<IHostedService>().OfType<BackgroundServiceA>().Single());
services.AddSingleton<BackgroundServiceB>(p => p.GetServices<IHostedService>().OfType<BackgroundServiceB>().Single());
Edit: It's probably better to implement this the other way around;
services.AddSingleton<BackgroundServiceA>();
services.AddSingleton<IHostedService>(p => p.GetService<BackgroundServiceA>());
Try to register them via AddHostedService call in separate ConfigureServices call outside startup, as stated in docs:
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServices(services =>
{
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
});
avoid direct access to hosted service as discussed here
use intermediate service instead as describe in 'Queued background tasks' example
I have some services that were initially designed to be called from my ASP.NET Core 2.1 web application. They have dependencies injected to their constructors using Microsoft.Extensions.DependencyInjection package stuff. Some of them have a dependency of ILogger logger.
public GroupService(ILogger<GroupService> logger)
{
...
}
I am building a service provider within the function so that they can still work as expected however I'm wondering what I should do about the logger dependencies. An Azure Function (V2) gets an ILogger injected into it by default but that can't be used in the DI container to create the additional loggers that the services require.
Is there a LoggerFactory registered somewhere "under the covers" that I can get access to to be used in my DI container? I think that would allow me to add additional loggers that log to the functions output window or am I completely misunderstanding how logging in a function would work?
Do I just need to set up a new LoggerFactory and make that log to the ApplicationInsights instance used by the functions project?
Using the most recent Azure Function runtime, you can now have the runtime inject your dependencies through constructors.
You can now remove the static keywords from your Azure Function and have the runtime.
Here is how you can set this up:
[assembly: WebJobsStartup(typeof(StartUp))]
public class StartUp : IWebJobsStartup
{
public void Configure(IWebJobsBuilder webJobsBuilder)
{
// Enables logging and sets the minimum level of logs.
webJobsBuilder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.SetMinimumLevel(LogLevel.Debug);
});
// Registers your services.
webJobsBuilder.Services.AddTransient<IGroupService, GroupService>();
}
}
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.