UPDATE - Fix
Added the services and directly injected them into the constructors.
Implemented service and application option registrations as extensions.
I have a tag helper that rely on IServiceCollection to be injected for requesting services, but the collection doesn't contain any elements even though I've registered two services in Startup.
The services gets registered in an asp.net core web application that references a class library which contains the tag helper, so it's two separate projects.
I also had to register IServiceCollection as a service since it fails without (missing service in the tag helper when trying to request IRazorViewEngine), but I thought the collection was automatically registered. It seems strange to add a collection to another collection of the same type..?
Everything is Core 2.0
Web application, Startup.cs:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServiceCollection, ServiceCollection>();
services.AddSingleton<IRazorViewEngine, RazorViewEngine>();
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Main}/{action=Index}/{id?}");
});
}
public IConfiguration Configuration { get; }
Class library, ViewResourceTagHelper.cs:
private IHttpContextAccessor _http;
private IServiceCollection _services;
public ViewResourceTagHelper(IHostingEnvironment env, IServiceCollection services) : base(env)
{
using (ServiceProvider provider = services.BuildServiceProvider())
{
_http = provider.GetRequiredService<IHttpContextAccessor>();
}
_services = services;
}
You should inject a IServiceProvider instance rather than a IServiceCollection instance:
public ViewResourceTagHelper(IHostingEnvironment env, IServiceProvider provider) : base(env)
{
_http = provider.GetRequiredService<IHttpContextAccessor>();
}
Injecting a IServiceCollection makes no sense since it's used to configure the container. The IServiceProvider is the container which resolves services for you. The ServiceCollection instance which gets injected in empty because DI just news up a new and empty instance for you.
I wonder though, why do you not inject a IHttpContextAccessor instance directly? By the way, you have to register an instance for IHttpContextAccessor as well, as it's not registered by default:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Take a look at the docs for more information about Dependency Injection in ASP.NET Core.
Related
Well, I have created an application to start on ASP net core 3.1 from scratch, I have created an API application and I have already created some layers to have better control on my application, However, when I created my object with its interface and registered them in the startup file on this way:
services.AddScoped<IMySpaceService, MySpaceService>();
I have gotten this error when I run the application:
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MySpaceService.Services.Interfaces.IMySpaceService Lifetime: Scoped ImplementationType:
this is my code:
public class MySpaceService: IMySpaceService
{
private IMySpaceRepository _mySpaceRepository;
public MySpaceService(IMySpaceRepository mySpaceRepository)
{
_mySpaceRepository = mySpaceRepository;
}
public IList<MySpaceDto> getSpaces()
{
List<MySpaceDto> spaces = new List<MySpaceDto>();
var data = _mySpaceRepository.getSpaces();
foreach (var item in data)
{
SpaceDto spaceDto = new SpaceDto();
spaceDto.Identification = item.Identification;
spaceDto.Name = item.Name;
spaces.Add(spaceDto);
}
return spaces;
}
}
My startup code:
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.AddControllers();
services.AddScoped<IMySpaceService, MySpaceService>();
services.AddScoped<IMySpaceRepository, MySpaceRepository>();
}
// 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.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Any Ideas about it?.
Your MySpaceService has only one constructor with parameter IMySpaceRepository. You need to register your repository as well:
services.AddScoped<IMySpaceRepository, MySpaceRepository>();
services.AddScoped<IMySpaceService, MySpaceService>();
Well, definitely the problem was that I had not registered yet a dependency, however, the dependency that I hadn't registered was "Dbcontext" and I am calling it from my repository class on the constructor. Therefore, I have to say that your comments helped me to solve my problem because finally, it was a problem with the dependency that didn't register.
I had to do this on my startup file:
services.AddDbContext<ExampleContext>(
options => options.UseMySql("Server=localhost;port=3306;Database=exampleDB;User=UserRegistered;Password=*******", mySqlOptions => mySqlOptions
.ServerVersion(new ServerVersion(new Version(8, 0, 18), ServerType.MySql))));
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.");
I have the following situation:
startup.cs
services.AddSingleton<IConfigurationManager, ConfigurationManager>();
ConfigurationManager configurationManager = new ConfigurationManager();
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(configurationManager.DatabaseConnectionString));
So, in order to create my context I need the connection string which this configurationManager provides to me. However, I would still like to keep the ConfigurationManager as a service.
Is there a way to do this without explicitly instantiating the configurationManager or is it perhaps even fine to leave it like this?
It's possible to access the IServiceProvider while building the context:
services.AddDbContext<MyContext>((serviceProvider, options) =>
{
var configManager = serviceProvider.GetService<IConfigurationManager>();
options.UseSqlServer(configManager.DatabaseConnectionString);
});
However, here your best options might be to read the Iconfiguration injected in Startup.cs:
public class Startup
{
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
HostingEnvironment = env;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("MyContext"));
});
}
}
You can use the service provider to get instances of services:
Build the services provider -
var provider = services.BuildServiceProvider();
Get specific service -
provider.GetService<T>();
Although a better option would be to use the IConfiguration .NET Core provides - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1
Using this method you can set IConfiguration to parse your config settings in the Startup method. From there you can then inject these settings into the required classes.
Also you can do sth similar to this.
I am not familiar with what you do with your configuration manager to provide a precise answer.
Basically you can do a pre-config inside your Program.cs.
Build your configuration here. As you can see i am passing IConfigurationBuilder.
Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(AddDbConfiguration)
.UseStartup<Startup>();
private static void AddDbConfiguration(WebHostBuilderContext context, IConfigurationBuilder builder)
{
var configuration = builder.Build(); // you could instantiate you Configuration manager here and append to the configuration.
var connectionString = configuration.GetConnectionString("Database");
builder.AddDemoDbProvider(options => options.UseSqlServer(connectionString));
}
source: https://medium.com/#dneimke/custom-configuration-in-net-core-2-193ff6f02046
I'm trying to get some of my services in the configure method so I can seed my database easier with them.
So I've injected IServiceProvider in my Configure method but every time I do : var service = serviceProvider.GetService<UserService>(); it returns null...
Here's my code :
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(this.Configuration.GetConnectionString("DbConnectionString")));
// Add framework services.
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<IdentityDbContext>().AddDefaultTokenProviders();
string connection = this.Configuration.GetConnectionString("DbConnectionString");
services.AddEntityFramework();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connection));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<AppSettings>(this.Configuration.GetSection("AppSettings"));
services.AddScoped<IsAuthorized>();
services.AddSingleton<UserManager<ApplicationUser>>();
services.AddTransient<IUsersService, UserService>();
services.AddMvc(config => { config.Filters.Add(typeof(SingletonUsers)); });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider services)
{
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
// This returns the context.
var context = services.GetService<ApplicationDbContext>();
// This returns null.
var userService = services.GetService<UserService>();
// Can't work without the UserService
MyDbInitializer.Initialize(context, userService);
}
}
as you registered UserService using
services.AddTransient<IUsersService, UserService>();
instead of
var userService = services.GetService<UserService>();
you need to ask for interface type:
var userService = services.GetService<IUserService>();
As an alternative to injecting IServiceProvider, you can just request the services as parameters to Configure:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
ApplicationDbContext context,
IUserService userService)
{
}
IServiceProvider is not registered with the Di/IoC, because the IServiceProvider is the IoC/DI (or a wrapper around it, when using 3rd party DI).
Once a IServiceProvider is created, it's dependency configuration can't be changed anymore (this applies to the out of the box IoC).
When you need a dependency in Configure you should pass this dependency as the method parameter (method injection) and get the instance. If you still need to access the IServiceProvider, you can do so by calling app.ApplicationServices.
But be aware, when you use app.ApplicationServices, you are resolving from the application-wide container. Every scoped or transient service resolved this way, will stay active until the application ends. This is because during application startup there is no scoped container, it is created during a request.
This means, you have to create a scope within Configure method, instantiate the services you need, call them and then dispose the scoped context before you leave it.
// Example of EF DbContext seeding I use in my application
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
using (var context = scope.ServiceProvider.GetRequiredService<MyDbContext>())
{
if(context.Database.EnsureCreated())
{
context.SeedAsync().Wait();
}
}
}
This makes sure that all services are clearly disposed and you have no memory leaks. This can have severe impact if/when you initialize DbContext without creating a scoped container, like turning DbContext into a singleton (because it will be resolved from parent container) or causing memory leaks because services resolved in application scope remain active for the lifetime of the application.
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>();
}