Cannot resolve scoped service - c#

I have problem with understanding source of errors in my code. I try to get throw course about microservices in .net core. After running build solution I get:
------- Project finished: CrossX.Services.Identity. Succeeded: True. Errors: 0. Warnings: 0
But when I run it I get:
/opt/dotnet/dotnet /RiderProjects/crossx/src/CrossX.Services.Identity/bin/Debug/netcoreapp2.2/CrossX.Services.Identity.dll
Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service 'CrossX.NETCore.Commands.ICommandHandler`1[CrossX.NETCore.Commands.CreateUser]' from root provider.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at CrossX.NETCore.Services.ServiceHost.BusBuilder.SubscribeToCommand[TCommand]() in /RiderProjects/crossx/src/CrossX.NETCore/Services/ServiceHost.cs:line 78
at CrossX.Services.Identity.Program.Main(String[] args) in /RiderProjects/crossx/src/CrossX.Services.Identity/Program.cs:line 11
When I added to webHostBuilder .UseDefaultServiceProvider(options => options.ValidateScopes = false) my problem was solved. But turning off validations isn't good idea from what I know. Also When I changed AddScope to AddTransient problem was solved (or at least it run).
Problem is that I have no idea where to look for source of this error. I guess I lack of understanding what is wrong, so I would appreciate if someone would help me, or at least give a hint.
Here is my
Startup.cs:
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)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddRabbitMq(Configuration);
services.AddScoped<ICommandHandler<CreateUser>, CreateUserHandler>();
services.AddScoped<IEncrypter, Encrypter>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
ServiceHost.Create<Startup>(args)
.UseRabbitMq()
.SubscribeToCommand<CreateUser>()
.Build()
.Run();
}
}
ServiceHost.cs
public class ServiceHost : IServiceHost
{
private readonly IWebHost _webHost;
public ServiceHost(IWebHost webHost)
{
_webHost = webHost;
}
public void Run() => _webHost.Run();
public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
{
Console.Title = typeof(TStartup).Namespace;
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
var webHostBuilder = WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
// .UseDefaultServiceProvider(options => options.ValidateScopes = false)
.UseStartup<TStartup>();
return new HostBuilder(webHostBuilder.Build());
}
public abstract class BuilderBase
{
public abstract ServiceHost Build();
}
public class HostBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public HostBuilder(IWebHost webHost)
{
_webHost = webHost;
}
public BusBuilder UseRabbitMq()
{
_bus = (IBusClient) _webHost.Services.GetService(typeof(IBusClient));
return new BusBuilder(_webHost, _bus);
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
public class BusBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public BusBuilder(IWebHost webHost, IBusClient bus)
{
_webHost = webHost;
_bus = bus;
}
public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
{
var handler = (ICommandHandler<TCommand>) _webHost.Services.GetService(typeof(ICommandHandler<TCommand>));
_bus.WithCommandHandlerAsync(handler);
return this;
}
public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
{
var handler = (IEventHandler<TEvent>) _webHost.Services.GetService(typeof(IEventHandler<TEvent>));
_bus.WithEventHandlerAsync(handler);
return this;
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
}

Cannot resolve scoped service ICommandHandler<CreateUser> from root provider
As the error says, you cannot create a scoped service instance from the root provider. The root provider is the root service provider that exists outside of service scopes. As such, it cannot resolve services that should only be consumed within service scopes.
If you want to resolve a scoped service from the root provider, for example when you are consuming it from a singleton service, you should create a service scope first using the IServiceScopeFactory:
var serviceScopeFactory = _webHost.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
var handler = (IEventHandler<TEvent>)scope.ServiceProvider.GetService(typeof(IEventHandler<TEvent>))
// …
}
Note that service scopes are supposed to be short lived, and that you need to dispose them afterwards to clean up.
Looking at your implementation, it seems as if you pass your scoped services to some other service in order to subscribe to events. This generally seems like a bad idea since that means that a reference to a scoped service will be kept by a (likely) singleton service for the whole lifetime of the application. This is generally a bad idea (as I said, scoped services are supposed to live only a short time).
You should ask yourself why you need the services to be scoped there, and whether you cannot make them singleton instead. Or if you actually need the subscribe mechanism to be based on the instance (instead of for example just the type, using a factory pattern or something).

Related

.NET Core HealthCheck - Add HealthCheck with dependency injection and parameters

I have different classes which inherit of a base class. The base class implements the interface IHealthCheck. Each class has a constructor which need a logger and parameters according to the class.
For example :
public ConnectionHealthCheck(ILogger logger, string address)
: base(logger)
{
Address = address;
}
I have a appSettings.json which allows me to configure several diagnostics to do in my Health Check service.
I get the list of diagnostics in my App.xaml.cs and i'm trying to add them in the HealthCheck list.
The problem is that I cannot do a dependency injection with parameters next to it and I don't know what is the best solution to do it...
Here is some parts of my code.
The OnStartup method :
protected override void OnStartup(StartupEventArgs e)
{
var a = Assembly.GetExecutingAssembly();
using var stream = a.GetManifestResourceStream("appsettings.json");
Configuration = new ConfigurationBuilder()
.AddJsonStream(stream)
.Build();
var host = new HostBuilder()
.ConfigureHostConfiguration(c => c.AddConfiguration(Configuration))
.ConfigureServices(ConfigureServices)
.ConfigureLogging(ConfigureLogging)
.Build();
[...] }
The configureService Method :
private void ConfigureServices(IServiceCollection serviceCollection)
{
// create and add the healthCheck for each diag in the appSettings file
List<DiagnosticConfigItem> diagnostics = Configuration.GetSection("AppSettings:Diagnostics").Get<List<DiagnosticConfigItem>>();
diagnostics.ForEach(x => CreateHealthCheck(serviceCollection, x));
[...] }
And the method CreateHealthCheck where is the problem :
private void CreateHealthCheck(IServiceCollection serviceCollection, DiagnosticConfigItem configItem)
{
EnumDiagType type;
try
{
type = (EnumDiagType)Enum.Parse(typeof(EnumDiagType), configItem.Type, true);
}
catch (Exception)
{
throw new Exception("Diagnostic type not supported");
}
switch (type)
{
case EnumDiagType.Connection:
serviceCollection.AddHealthChecks().AddCheck(nameof(ConnectionHealthCheck), new ConnectionHealthCheck(???, configItem.Value));
break;
case EnumDiagType.Other:
[...] }
As you can see, I cannot create the instance of the ConnectionHealthCheck class because I cannot reach the ILogger object...
So how can I do it ? I think about different solutions but I don't have the answer or the way to do it
Build the HealthCheck service not in the App.xaml.cs but after ? (In a view model for exemple where I have access to the serviceCollection and the logger)
Find a way to get the logger to use it in the CreateHealthCheck method ?
Do something like that but I don't know when I can pass the parameters
serviceCollection.AddHealthChecks().AddCheck<ConnectionHealthCheck>(nameof(ConnectionHealthCheck));
You can use HealthCheckRegistration to register your class (it should implement IHealthCheck), it has constructors accepting delegate Func<IServiceProvider,IHealthCheck> which allows you to use IServiceProvider to resolve required parameters to create an instance of your healthcheck class. Something like this:
public static class ConnectionHealthCheckBuilderExtensions
{
const string DefaultName = "example_health_check";
public static IHealthChecksBuilder AddConnectionHealthCheck(
this IHealthChecksBuilder builder,
string name,
DiagnosticConfigItem configItem,
HealthStatus? failureStatus = default,
IEnumerable<string> tags = default)
{
return builder.Add(new HealthCheckRegistration(
name ?? DefaultName,
sp => new ConnectionHealthCheck(sp.GetRequiredService<ISomeService>(), configItem.Value),
failureStatus,
tags));
}
}
See this part of docs for more details.
The .NET Core in-built DI can inject the components on the Constructor level.
So use the following way, which I use in my ASP.NET Core Projects.
public class Startup
{
public Startup(IWebHostEnvironment environment, IConfiguration configuration, ILoggerFactory loggerFactory)
{
Environment = environment;
Configuration = configuration;
LoggerFactory = loggerFactory;
}
public IConfiguration Configuration { get; }
public ILoggerFactory LoggerFactory { get; }
public IWebHostEnvironment Environment { get; }
private void ConfigureServices(IServiceCollection serviceCollection)
{
List<DiagnosticConfigItem> diagnostics = Configuration.GetSection("AppSettings:Diagnostics").Get<List<DiagnosticConfigItem>>();
diagnostics.ForEach(x => CreateHealthCheck(serviceCollection, x, LoggerFactory));
}
private void CreateHealthCheck(IServiceCollection serviceCollection, DiagnosticConfigItem configItem)
{
// Create a ILogger<T> based on your Type by
loggerFactory.CreateLogger<MessagingServices>())
}
}
This might be crude, but hope this helps.

ASP.NET Core 3.1 Logging at Startup [duplicate]

In order to debug a .NET Core app which is failing on startup, I would like to write logs from within the startup.cs file. I have logging setup within the file that can be used in the rest of the app outside the startup.cs file, but not sure how to write logs from within the startup.cs file itself.
.Net Core 3.1
Unfortunately, for ASP.NET Core 3.0, the situation is again a bit different. The default templates use the HostBuilder (instead of the WebHostBuilder) which sets up a new generic host that can host several different applications, not limited to web applications. Part of this new host is also the removal of the second dependency injection container that previously existed for the web host. This ultimately means that you won’t be able to inject any dependencies apart from the IConfiguration into the Startup class. So you won’t be able to log during the ConfigureServices method. You can, however, inject the logger into the Configure method and log there:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
logger.LogInformation("Configure called");
// …
}
If you absolutely need to log within ConfigureServices, then you can continue to use the WebHostBuilder which will create the legacy WebHost that can inject the logger into the Startup class. Note that it’s likely that the web host will be removed at some point in the future. So you should try to find a solution that works for you without having to log within ConfigureServices.
.NET Core 2.x
This has changed significantly with the release of ASP.NET Core 2.0. In ASP.NET Core 2.x, logging is created at the host builder. This means that logging is available through DI by default and can be injected into the Startup class:
public class Startup
{
private readonly ILogger<Startup> _logger;
public IConfiguration Configuration { get; }
public Startup(ILogger<Startup> logger, IConfiguration configuration)
{
_logger = logger;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices called");
// …
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
_logger.LogInformation("Configure called");
// …
}
}
Option 1: Directly use log (e.g. Serilog) in startup-
public class Startup
{
public Startup(IHostingEnvironment env)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath, "Serilog-{Date}.txt"))
.CreateLogger();
Log.Information("Inside Startup ctor");
....
}
public void ConfigureServices(IServiceCollection services)
{
Log.Information("ConfigureServices");
....
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
Log.Information("Configure");
....
}
Output:
To setup Serilog in asp.net-core application, check out the Serilog.AspNetCore package on GitHub.
Option2: Configure logging in program.cs like this-
var host = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(s => {
s.AddSingleton<IFormatter, LowercaseFormatter>();
})
.ConfigureLogging(f => f.AddConsole(LogLevel.Debug))
.UseStartup<Startup>()
.Build();
host.Run();
User loggerFactory in startup like this-
public class Startup
{
ILogger _logger;
IFormatter _formatter;
public Startup(ILoggerFactory loggerFactory, IFormatter formatter)
{
_logger = loggerFactory.CreateLogger<Startup>();
_formatter = formatter;
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogDebug($"Total Services Initially: {services.Count}");
// register services
//services.AddSingleton<IFoo, Foo>();
}
public void Configure(IApplicationBuilder app, IFormatter formatter)
{
// note: can request IFormatter here as well as via constructor
_logger.LogDebug("Configure() started...");
app.Run(async (context) => await context.Response.WriteAsync(_formatter.Format("Hi!")));
_logger.LogDebug("Configure() complete.");
}
}
Complete details available on this link
In .NET Core 3.1, you can create a logger directly using LogFactory.
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
The official solution is currently to setup a local LoggerFactory like this:
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddConsole();
builder.AddEventSourceLogger();
});
var logger = loggerFactory.CreateLogger("Startup");
logger.LogInformation("Hello World");
See also: https://github.com/dotnet/aspnetcore/issues/9337#issuecomment-539859667
For .Net 6
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
var logger = ((IApplicationBuilder)app).ApplicationServices.GetService<ILogger<Program>>();
logger.LogInformation("Some logs");
Or even more easy way:
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
ILogger logger = app.Logger;
Using Rolf's answer, I put this in my Startup constructor:
private readonly ILogger _logger;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddConsole();
builder.AddEventSourceLogger();
});
_logger = loggerFactory.CreateLogger<Startup>();
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices...");
// ...and so on...
}
For .NET Core 3.0 the official docs has this to say: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup
Writing logs before completion of the DI container setup in the Startup.ConfigureServices method is not supported:
Logger injection into the Startup constructor is not supported.
Logger injection into the Startup.ConfigureServices method signature is not supported
But as they say in the docs you can configure a service that depends on ILogger, so if you wrote a class StartupLogger:
public class StartupLogger
{
private readonly ILogger _logger;
public StartupLogger(ILogger<StartupLogger> logger)
{
_logger = logger;
}
public void Log(string message)
{
_logger.LogInformation(message);
}
}
Then in Startup.ConfigureServices add the service, then you need to build the service provider to get access to the DI container:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(provider =>
{
var service = provider.GetRequiredService<ILogger<StartupLogger>>();
return new StartupLogger(service);
});
var logger = services.BuildServiceProvider().GetRequiredService<StartupLogger>();
logger.Log("Startup.ConfigureServices called");
}
Edit: this produces a compiler warning, for the sake of debugging your StartUp class this should be OK but not for production:
Startup.cs(39, 32): [ASP0000] Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.
None of the existing answers worked for me. I'm using NLog, and even building a new ServiceCollection, calling .CreateBuilder() on any service collection, creating a logging service ... none of that would write to a log file during ConfigureServices.
The problem is that logging isn't really a thing until after the ServiceCollection is built, and it's not built during ConfigureServices.
Basically, I just want (need) to log what's going on during startup in a configuration extension method, because the only tier I'm having a problem on is PROD, where I can't attach a debugger.
The solution that worked for me was using the old .NET Framework NLog method:
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
Added that right to the extension method class, and I was able to write to a log ("the" log) during ConfigureServices and after.
I have no idea if this is a good idea to actually release into production code (I don't know if the .NET controlled ILogger and this NLog.ILogger will conflict at any point), but I only needed it to see what was going on.
I use a solution avoiding 3rd party loggers implementing a "logger buffer" with ILogger interface.
public class LoggerBuffered : ILogger
{
class Entry
{
public LogLevel _logLevel;
public EventId _eventId;
public string _message;
}
LogLevel _minLogLevel;
List<Entry> _buffer;
public LoggerBuffered(LogLevel minLogLevel)
{
_minLogLevel = minLogLevel;
_buffer = new List<Entry>();
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= _minLogLevel;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel)) {
var str = formatter(state, exception);
_buffer.Add(new Entry { _logLevel = logLevel, _eventId = eventId, _message = str });
}
}
public void CopyToLogger (ILogger logger)
{
foreach (var entry in _buffer)
{
logger.Log(entry._logLevel, entry._eventId, entry._message);
}
_buffer.Clear();
}
}
Usage in startup.cs is easy, of course you get log output after call of Configure. But better than nothing. :
public class Startup
{
ILogger _logger;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
_logger = new LoggerBuffered(LogLevel.Debug);
_logger.LogInformation($"Create Startup {env.ApplicationName} - {env.EnvironmentName}");
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices");
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
(_logger as LoggerBuffered).CopyToLogger(logger);
_logger = logger; // Replace buffered by "real" logger
_logger.LogInformation("Configure");
if (env.IsDevelopment())
Main code:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
CreateDefaultBuilder sets up a default console logger.
... configures the ILoggerFactory to log to the console and debug output
Startup code:
using Microsoft.Extensions.Logging;
...
public class Startup
{
private readonly ILogger _logger;
public Startup(IConfiguration configuration, ILoggerFactory logFactory)
{
_logger = logFactory.CreateLogger<Startup>();
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)
{
_logger.LogInformation("hello stackoverflow");
}
I couldn't get the injection of an ILogger to work, but perhaps that's because it's not a controller. More info welcome!
Refs:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1&tabs=aspnetcore2x
Are you making decisions about which services you are using at runtime that you wish to log? Or are you making decisions about how those services are configured, which you wish to log?
In other words;
public void ConfigureServices(IServiceCollection services){
// Do you really want to log something here?
services.AddRazorPages(options => {
// Or would you be satisfied by logging something here?
});
}
If it is only the latter, you can move the implementation of these lambda functions into an IConfigureOptions<T> service, allowing you to inject other services. Continuing the above example, you could create the following;
public class ConfigureRazorPagesOptions : IConfigureOptions<RazorPagesOptions>
{
private readonly ILogger<ConfigureRazorPagesOptions> logger;
public ConfigureRazorPagesOptions(ILogger<ConfigureRazorPagesOptions> logger)
{
this.logger = logger;
}
public void Configure(RazorPagesOptions options)
{
logger.LogInformation("Now I can log here!");
}
}
public void ConfigureServices(IServiceCollection services){
services.AddRazorPages();
services.AddSingleton<IConfigureOptions<RazorPagesOptions>, ConfigureRazorPagesOptions>();
}
If your .ConfigureServices method is getting too complicated, you might want to create such services. However, that's a lot of boilerplate to add for each options type. There is also an equivalent shorthand, to inject other services into a configuration lamda;
services.AddOptions<RazorPagesOptions>()
.Configure<ILogger<RazorPagesOptions>>((options, logger) => {
logger.LogInformation("I can log here too!");
});
This worked for me
private static readonly Logger logger = LogManager.GetLogger("Audit")
I found a very easy implementation:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var conn = Configuration.GetValue("conn", Configuration.GetConnectionString("Conn"));
Console.WriteLine($#"System starting at {DateTime.Now}");
Console.WriteLine($#"Database: {conn}");
}
Just using Console.WriteLine worked, even on Docker.
Just use the line below for logging in Startup.cs
Log.Information("App started.");

Dependency injection within ASP.Net Core

Been doing some sample code with ASP.NET Core to try to understand how it fits together and I am stumped as to why I am unable to successfully resolve a service.
The configure services method has the call to add ISeedDataService
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<CustomerDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ICustomerDbContext, CustomerDbContext>();
services.AddScoped<ICustomerRepository, CustomerRepository>();
services.AddScoped<ISeedDataService, SeedDataService>();
}
In Configure I am calling AddSeedData() as below
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.AddSeedData();
}
which is calling the extension method below
public static async void AddSeedData(this IApplicationBuilder app)
{
var seedDataService = app.ApplicationServices.GetRequiredService<ISeedDataService>();
await seedDataService.EnsureSeedData();
}
and the SeedDataService is below
public class SeedDataService : ISeedDataService
{
private ICustomerDbContext _context;
public SeedDataService(ICustomerDbContext context)
{
_context = context;
}
public async Task EnsureSeedData()
{
_context.Database.EnsureCreated();
_context.Customers.RemoveRange(_context.Customers);
_context.SaveChanges();
Customer customer = new Customer();
customer.FirstName = "Chuck";
customer.LastName = "Norris";
customer.Age = 30;
customer.Id = Guid.NewGuid();
_context.Add(customer);
Customer customer2 = new Customer();
customer2.FirstName = "Fabian";
customer2.LastName = "Gosebrink";
customer2.Age = 31;
customer2.Id = Guid.NewGuid();
_context.Add(customer2);
await _context.SaveChangesAsync();
}
}
Totally unsure as to what I am doing wrong, the error is System.InvalidOperationException: 'Cannot resolve scoped service 'secondapp.Services.ISeedDataService' from root provider.'
You are (and should be) adding the ISeedDataService as scoped service. However, you are attempting to resolve it from the root service provider (e.g. app.ApplicationServices) which is not scoped. This means that scoped services resolved from it effectively are turned into a singleton service and are not disposed until the application shuts down or it will result in an error.
The solution here is to create a scope yourself:
public void Configure(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.CreateScope())
{
var seedDataService = scope.ServiceProvider.GetRequiredService<ISeedDataService>();
// Use seedDataService here
}
}
Please take a look at the documentation regarding dependency injection scopes.
On a second note: your AddSeedData extension method is async void and you are not waiting for the result. You should return a task (async Task) call AddSeedData().GetAwaiter().GetResult() to make sure you block until the seeding is complete.
The Configure() method allows parameter dependency injection so you can do the following.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ISeedDataService seedService)
{
seedService.EnsureSeedData().Wait(); // Configure() is not async so you have to wait
}

How do I write logs from within Startup.cs?

In order to debug a .NET Core app which is failing on startup, I would like to write logs from within the startup.cs file. I have logging setup within the file that can be used in the rest of the app outside the startup.cs file, but not sure how to write logs from within the startup.cs file itself.
.Net Core 3.1
Unfortunately, for ASP.NET Core 3.0, the situation is again a bit different. The default templates use the HostBuilder (instead of the WebHostBuilder) which sets up a new generic host that can host several different applications, not limited to web applications. Part of this new host is also the removal of the second dependency injection container that previously existed for the web host. This ultimately means that you won’t be able to inject any dependencies apart from the IConfiguration into the Startup class. So you won’t be able to log during the ConfigureServices method. You can, however, inject the logger into the Configure method and log there:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
logger.LogInformation("Configure called");
// …
}
If you absolutely need to log within ConfigureServices, then you can continue to use the WebHostBuilder which will create the legacy WebHost that can inject the logger into the Startup class. Note that it’s likely that the web host will be removed at some point in the future. So you should try to find a solution that works for you without having to log within ConfigureServices.
.NET Core 2.x
This has changed significantly with the release of ASP.NET Core 2.0. In ASP.NET Core 2.x, logging is created at the host builder. This means that logging is available through DI by default and can be injected into the Startup class:
public class Startup
{
private readonly ILogger<Startup> _logger;
public IConfiguration Configuration { get; }
public Startup(ILogger<Startup> logger, IConfiguration configuration)
{
_logger = logger;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices called");
// …
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
_logger.LogInformation("Configure called");
// …
}
}
Option 1: Directly use log (e.g. Serilog) in startup-
public class Startup
{
public Startup(IHostingEnvironment env)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath, "Serilog-{Date}.txt"))
.CreateLogger();
Log.Information("Inside Startup ctor");
....
}
public void ConfigureServices(IServiceCollection services)
{
Log.Information("ConfigureServices");
....
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
Log.Information("Configure");
....
}
Output:
To setup Serilog in asp.net-core application, check out the Serilog.AspNetCore package on GitHub.
Option2: Configure logging in program.cs like this-
var host = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(s => {
s.AddSingleton<IFormatter, LowercaseFormatter>();
})
.ConfigureLogging(f => f.AddConsole(LogLevel.Debug))
.UseStartup<Startup>()
.Build();
host.Run();
User loggerFactory in startup like this-
public class Startup
{
ILogger _logger;
IFormatter _formatter;
public Startup(ILoggerFactory loggerFactory, IFormatter formatter)
{
_logger = loggerFactory.CreateLogger<Startup>();
_formatter = formatter;
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogDebug($"Total Services Initially: {services.Count}");
// register services
//services.AddSingleton<IFoo, Foo>();
}
public void Configure(IApplicationBuilder app, IFormatter formatter)
{
// note: can request IFormatter here as well as via constructor
_logger.LogDebug("Configure() started...");
app.Run(async (context) => await context.Response.WriteAsync(_formatter.Format("Hi!")));
_logger.LogDebug("Configure() complete.");
}
}
Complete details available on this link
In .NET Core 3.1, you can create a logger directly using LogFactory.
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
The official solution is currently to setup a local LoggerFactory like this:
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddConsole();
builder.AddEventSourceLogger();
});
var logger = loggerFactory.CreateLogger("Startup");
logger.LogInformation("Hello World");
See also: https://github.com/dotnet/aspnetcore/issues/9337#issuecomment-539859667
For .Net 6
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
var logger = ((IApplicationBuilder)app).ApplicationServices.GetService<ILogger<Program>>();
logger.LogInformation("Some logs");
Or even more easy way:
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
ILogger logger = app.Logger;
Using Rolf's answer, I put this in my Startup constructor:
private readonly ILogger _logger;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddConsole();
builder.AddEventSourceLogger();
});
_logger = loggerFactory.CreateLogger<Startup>();
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices...");
// ...and so on...
}
For .NET Core 3.0 the official docs has this to say: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup
Writing logs before completion of the DI container setup in the Startup.ConfigureServices method is not supported:
Logger injection into the Startup constructor is not supported.
Logger injection into the Startup.ConfigureServices method signature is not supported
But as they say in the docs you can configure a service that depends on ILogger, so if you wrote a class StartupLogger:
public class StartupLogger
{
private readonly ILogger _logger;
public StartupLogger(ILogger<StartupLogger> logger)
{
_logger = logger;
}
public void Log(string message)
{
_logger.LogInformation(message);
}
}
Then in Startup.ConfigureServices add the service, then you need to build the service provider to get access to the DI container:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(provider =>
{
var service = provider.GetRequiredService<ILogger<StartupLogger>>();
return new StartupLogger(service);
});
var logger = services.BuildServiceProvider().GetRequiredService<StartupLogger>();
logger.Log("Startup.ConfigureServices called");
}
Edit: this produces a compiler warning, for the sake of debugging your StartUp class this should be OK but not for production:
Startup.cs(39, 32): [ASP0000] Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.
None of the existing answers worked for me. I'm using NLog, and even building a new ServiceCollection, calling .CreateBuilder() on any service collection, creating a logging service ... none of that would write to a log file during ConfigureServices.
The problem is that logging isn't really a thing until after the ServiceCollection is built, and it's not built during ConfigureServices.
Basically, I just want (need) to log what's going on during startup in a configuration extension method, because the only tier I'm having a problem on is PROD, where I can't attach a debugger.
The solution that worked for me was using the old .NET Framework NLog method:
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
Added that right to the extension method class, and I was able to write to a log ("the" log) during ConfigureServices and after.
I have no idea if this is a good idea to actually release into production code (I don't know if the .NET controlled ILogger and this NLog.ILogger will conflict at any point), but I only needed it to see what was going on.
I use a solution avoiding 3rd party loggers implementing a "logger buffer" with ILogger interface.
public class LoggerBuffered : ILogger
{
class Entry
{
public LogLevel _logLevel;
public EventId _eventId;
public string _message;
}
LogLevel _minLogLevel;
List<Entry> _buffer;
public LoggerBuffered(LogLevel minLogLevel)
{
_minLogLevel = minLogLevel;
_buffer = new List<Entry>();
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= _minLogLevel;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel)) {
var str = formatter(state, exception);
_buffer.Add(new Entry { _logLevel = logLevel, _eventId = eventId, _message = str });
}
}
public void CopyToLogger (ILogger logger)
{
foreach (var entry in _buffer)
{
logger.Log(entry._logLevel, entry._eventId, entry._message);
}
_buffer.Clear();
}
}
Usage in startup.cs is easy, of course you get log output after call of Configure. But better than nothing. :
public class Startup
{
ILogger _logger;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
_logger = new LoggerBuffered(LogLevel.Debug);
_logger.LogInformation($"Create Startup {env.ApplicationName} - {env.EnvironmentName}");
}
public void ConfigureServices(IServiceCollection services)
{
_logger.LogInformation("ConfigureServices");
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
(_logger as LoggerBuffered).CopyToLogger(logger);
_logger = logger; // Replace buffered by "real" logger
_logger.LogInformation("Configure");
if (env.IsDevelopment())
Main code:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
CreateDefaultBuilder sets up a default console logger.
... configures the ILoggerFactory to log to the console and debug output
Startup code:
using Microsoft.Extensions.Logging;
...
public class Startup
{
private readonly ILogger _logger;
public Startup(IConfiguration configuration, ILoggerFactory logFactory)
{
_logger = logFactory.CreateLogger<Startup>();
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)
{
_logger.LogInformation("hello stackoverflow");
}
I couldn't get the injection of an ILogger to work, but perhaps that's because it's not a controller. More info welcome!
Refs:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1&tabs=aspnetcore2x
Are you making decisions about which services you are using at runtime that you wish to log? Or are you making decisions about how those services are configured, which you wish to log?
In other words;
public void ConfigureServices(IServiceCollection services){
// Do you really want to log something here?
services.AddRazorPages(options => {
// Or would you be satisfied by logging something here?
});
}
If it is only the latter, you can move the implementation of these lambda functions into an IConfigureOptions<T> service, allowing you to inject other services. Continuing the above example, you could create the following;
public class ConfigureRazorPagesOptions : IConfigureOptions<RazorPagesOptions>
{
private readonly ILogger<ConfigureRazorPagesOptions> logger;
public ConfigureRazorPagesOptions(ILogger<ConfigureRazorPagesOptions> logger)
{
this.logger = logger;
}
public void Configure(RazorPagesOptions options)
{
logger.LogInformation("Now I can log here!");
}
}
public void ConfigureServices(IServiceCollection services){
services.AddRazorPages();
services.AddSingleton<IConfigureOptions<RazorPagesOptions>, ConfigureRazorPagesOptions>();
}
If your .ConfigureServices method is getting too complicated, you might want to create such services. However, that's a lot of boilerplate to add for each options type. There is also an equivalent shorthand, to inject other services into a configuration lamda;
services.AddOptions<RazorPagesOptions>()
.Configure<ILogger<RazorPagesOptions>>((options, logger) => {
logger.LogInformation("I can log here too!");
});
This worked for me
private static readonly Logger logger = LogManager.GetLogger("Audit")
I found a very easy implementation:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var conn = Configuration.GetValue("conn", Configuration.GetConnectionString("Conn"));
Console.WriteLine($#"System starting at {DateTime.Now}");
Console.WriteLine($#"Database: {conn}");
}
Just using Console.WriteLine worked, even on Docker.
Just use the line below for logging in Startup.cs
Log.Information("App started.");

Resolving instances with ASP.NET Core DI from within ConfigureServices

How do I manually resolve a type using the ASP.NET Core MVC built-in dependency injection framework?
Setting up the container is easy enough:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
But how can I resolve ISomeService without performing injection? For example, I want to do this:
ISomeService service = services.Resolve<ISomeService>();
There are no such methods in IServiceCollection.
The IServiceCollection interface is used for building a dependency injection container. After it's fully built, it gets composed to an IServiceProvider instance which you can use to resolve services. You can inject an IServiceProvider into any class. The IApplicationBuilder and HttpContext classes can provide the service provider as well, via their ApplicationServices or RequestServices properties respectively.
IServiceProvider defines a GetService(Type type) method to resolve a service:
var service = (IFooService)serviceProvider.GetService(typeof(IFooService));
There are also several convenience extension methods available, such as serviceProvider.GetService<IFooService>() (add a using for Microsoft.Extensions.DependencyInjection).
Resolving services inside the startup class
Injecting dependencies
The runtime's hosting service provider can inject certain services into the constructor of the Startup class, such as IConfiguration,
IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.
The ConfigureServices() method does not allow injecting services, it only accepts an IServiceCollection argument. This makes sense because ConfigureServices() is where you register the services required by your application. However you can use services injected in the startup's constructor here, for example:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Use Configuration here
}
Any services registered in ConfigureServices() can then be injected into the Configure() method; you can add an arbitrary number of services after the IApplicationBuilder parameter:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFooService>();
}
public void Configure(IApplicationBuilder app, IFooService fooService)
{
fooService.Bar();
}
Manually resolving dependencies
If you need to manually resolve services, you should preferably use the ApplicationServices provided by IApplicationBuilder in the Configure() method:
public void Configure(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
It is possible to pass and directly use an IServiceProvider in the constructor of your Startup class, but as above this will contain a limited subset of services, and thus has limited utility:
public Startup(IServiceProvider serviceProvider)
{
var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}
If you must resolve services in the ConfigureServices() method, a different approach is required. You can build an intermediate IServiceProvider from the IServiceCollection instance which contains the services which have been registered up to that point:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFooService, FooService>();
// Build the intermediate service provider
var sp = services.BuildServiceProvider();
// This will succeed.
var fooService = sp.GetService<IFooService>();
// This will fail (return null), as IBarService hasn't been registered yet.
var barService = sp.GetService<IBarService>();
}
Please note:
Generally you should avoid resolving services inside the ConfigureServices() method, as this is actually the place where you're configuring the application services. Sometimes you just need access to an IOptions<MyOptions> instance. You can accomplish this by binding the values from the IConfiguration instance to an instance of MyOptions (which is essentially what the options framework does):
public void ConfigureServices(IServiceCollection services)
{
var myOptions = new MyOptions();
Configuration.GetSection("SomeSection").Bind(myOptions);
}
Or use an overload for AddSingleton/AddScoped/AddTransient:
// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
var fooService = sp.GetRequiredService<IFooService>();
return new BarService(fooService);
}
Manually resolving services (aka Service Locator) is generally considered an anti-pattern. While it has its use-cases (for frameworks and/or infrastructure layers), you should avoid it as much as possible.
Manually resolving instances involves using the IServiceProvider interface:
Resolving Dependency in Startup.ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IMyService>();
}
Resolving Dependencies in Startup.Configure
public void Configure(
IApplicationBuilder application,
IServiceProvider serviceProvider)
{
// By type.
var service1 = (MyService)serviceProvider.GetService(typeof(MyService));
// Using extension method.
var service2 = serviceProvider.GetService<MyService>();
// ...
}
Resolving Dependencies in Startup.Configure in ASP.NET Core 3
public void Configure(
IApplicationBuilder application,
IWebHostEnvironment webHostEnvironment)
{
application.ApplicationServices.GetService<MyService>();
}
Using Runtime Injected Services
Some types can be injected as method parameters:
public class Startup
{
public Startup(
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
{
}
public void ConfigureServices(
IServiceCollection services)
{
}
public void Configure(
IApplicationBuilder application,
IHostingEnvironment hostingEnvironment,
IServiceProvider serviceProvider,
ILoggerFactory loggerfactory,
IApplicationLifetime applicationLifetime)
{
}
}
Resolving Dependencies in Controller Actions
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
If you generate an application with a template you are going to have something like this on the Startup class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
You can then add dependencies there, for example:
services.AddTransient<ITestService, TestService>();
If you want to access ITestService on your controller you can add IServiceProvider on the constructor and it will be injected:
public HomeController(IServiceProvider serviceProvider)
Then you can resolve the service you added:
var service = serviceProvider.GetService<ITestService>();
Note that to use the generic version you have to include the namespace with the extensions:
using Microsoft.Extensions.DependencyInjection;
ITestService.cs
public interface ITestService
{
int GenerateRandom();
}
TestService.cs
public class TestService : ITestService
{
public int GenerateRandom()
{
return 4;
}
}
Startup.cs (ConfigureServices)
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddTransient<ITestService, TestService>();
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(IServiceProvider serviceProvider)
{
var service = serviceProvider.GetService<ITestService>();
int rnd = service.GenerateRandom();
}
If you just need to resolve one dependency for the purpose of passing it to the constructor of another dependency you are registering, you can do this.
Let's say you had a service that took in a string and an ISomeService.
public class AnotherService : IAnotherService
{
public AnotherService(ISomeService someService, string serviceUrl)
{
...
}
}
When you go to register this inside Startup.cs, you'll need to do this:
services.AddScoped<IAnotherService>(ctx =>
new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
You can inject dependencies in attributes like AuthorizeAttribute in this way
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
I know this is an old question but I'm astonished that a rather obvious and disgusting hack isn't here.
You can exploit the ability to define your own ctor function to grab necessary values out of your services as you define them... obviously this would be ran every time the service was requested unless you explicitly remove/clear and re-add the definition of this service within the first construction of the exploiting ctor.
This method has the advantage of not requiring you to build the service tree, or use it, during the configuration of the service. You are still defining how services will be configured.
public void ConfigureServices(IServiceCollection services)
{
//Prey this doesn't get GC'd or promote to a static class var
string? somevalue = null;
services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
//create service you need
var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
//get the values you need
somevalue = somevalue ?? service.MyDirtyHack();
//return the instance
return service;
});
services.AddTransient<IOtherService, OtherService>(scope => {
//Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
scope.GetService<IServiceINeedToUse>();
//TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
//Wow!
return new OtherService(somevalue);
});
}
The way to fix this pattern would be to give OtherService an explicit dependency on IServiceINeedToUse, rather than either implicitly depending on it or its method's return value... or resolving that dependency explicitly in some other fashion.
You can inject dependencies using IApplicationBuilder instance in this way
public void Configure(IApplicationBuilder app)
{
//---------- Your code
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var resultLogic = serviceScope.ServiceProvider.GetService<IResultLogic>();
resultLogic.YourMethod();
}
//---------- Your code
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISelfServiceConfigLoad, SelfServiceConfigLoader>();
var sp = services.BuildServiceProvider();
var configservice = sp.GetServices<ISelfServiceConfigLoad>();
services.AddSingleton<IExtractor, ConfigExtractor>( sp =>
{
var con = sp.GetRequiredService<ISelfServiceConfigLoad>();
var config = con.Load();
return new ConfigExtractor(config.Result);
});
services.AddSingleton<IProcessor<EventMessage>, SelfServiceProcessor>();
services.AddTransient<ISolrPush, SolrDataPush>();
services.AddSingleton<IAPICaller<string, string>, ApiRestCaller<string, string>>();
services.AddSingleton<IDataRetriever<SelfServiceApiRequest, IDictionary<string, object>>, SelfServiceDataRetriever>();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ConfigurationRepository>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
services.AddScoped<IConfigurationBL, ConfigurationBL>();
services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}

Categories