Antoine Thiry
Antoine Thiry

Reputation: 2442

asp net core requesting service in Configure method returns null

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);
        }
    } 

Upvotes: 26

Views: 33954

Answers (3)

Set
Set

Reputation: 49799

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>();

Upvotes: 37

Tseng
Tseng

Reputation: 64307

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.

Upvotes: 10

juunas
juunas

Reputation: 58908

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)
{

}

Upvotes: 17

Related Questions