Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
        }
    } 
like image 444
Antoine Thiry Avatar asked Mar 28 '17 09:03

Antoine Thiry


People also ask

What is the difference between GetService and GetRequiredService?

The difference is that GetService<T>() returns null if it can't find the service. GetRequiredService<T>() throws an InvalidOperationException instead.

When should I use IServiceProvider?

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.

How do I get an instance of IServiceProvider in .NET core?

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.

What is IServiceCollection in .NET core?

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.


3 Answers

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>();
like image 83
Set Avatar answered Oct 14 '22 04:10

Set


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

}
like image 16
juunas Avatar answered Oct 14 '22 03:10

juunas


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.

like image 9
Tseng Avatar answered Oct 14 '22 03:10

Tseng