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);
}
}
The difference is that GetService<T>() returns null if it can't find the service. GetRequiredService<T>() throws an InvalidOperationException instead.
The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container. The ServiceProvider ensures that resolved services live for the expected lifetime.
You can use the RequestServices property of the HttpContext class to retrieve an instance of type IServiceProvider and then use this instance to call the GetService method. The following code shows how this can be done. ICustomFileLogger logger = (ICustomFileLogger)HttpContext. RequestServices.
AddScoped(IServiceCollection, Type, Type) Adds a scoped service of the type specified in serviceType with an implementation of the type specified in implementationType to the specified IServiceCollection.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With