Junaid
Junaid

Reputation: 1030

Why is the IConfiguration object null after injecting it to DbContext? [ASP.NET Core 3.1]

Before posting this question I have gone through multiple posts that are similar. My question focuses on "why is it happening despite applying the common and possibly right solution?".

I am developing a .NET Core 3.1 web app. I have a DbContext named 'SkipQContext'. I am trying to access the connection string from appsettings.json in the SkipQContext file using Configuration object.

For that, I have injected IConfiguration as a service to the SkipQContext constructor.

Constructor:

private readonly string ConnectionString;

public SkipQContext()
{ 
}

public SkipQContext(DbContextOptions<SkipQContext> options, IConfiguration configuration)
    : base(options)
{
    ConnectionString = configuration.GetConnectionString("DefaultConnection");
}

I have also registered in ConfigureServices method of Startup class.

services.AddSingleton(Configuration);

Now when I instantiate SkipQContext in one of my repository classes, the default constructor of SkipQContext is called. When I try to fetch data using it, I get the "IConfiguration object is null."

I applied breakpoints in ConfigureServices method and can see that the IConfiguration object has the connection string value.

My first question is, why is it null in SkipQContext when I am registering it in ConfigureServices and also injecting it in SkipQContext constructor? Multiple answers online state this as the right method.

Also, I am thinking, I might not be instantiating the SkipQContext rightly. As my statement :

SkipQContext db = new SkipQContext();

hits the default constructor of SkipQContext which is empty and not the overloaded constructor where IConfiguration is injected.

P.S. If the last question is dumb. I am still a bit unclear about how dependency injection works in .NET Core.

Upvotes: 1

Views: 2285

Answers (2)

nalnpir
nalnpir

Reputation: 1197

There is no need to instantiate the Configuration as a Singleton, the Default builder of WebHost already inject the configuration in the request , your Startup Class should look like this

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

            string conn = Configuration.GetConnectionString("NAME OF YOUR CONNECTION STRING IN THE application.json FILE");
            

            services.AddDbContext<CLASSOFURDBCONTEXT>(config =>
            {
                config.UseSqlServer(conn);
            });

        }
    }

And your dbcontext should have the following constructor

        public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
        {
        }

Then you only need to call the DbContext in a controller or a service and the DI will do the rest

As per why your IConfiguration throw the null reference exception i can think of 2 possibilities. Either you need to do the other kind instanciation which would be like this

services.AddSingleton<IConfiguration,Configuration>();

Or maybe it is because you are not using DI into the DbContext itself, you shouldnt need to do the new YourContextDbContext(). You should just simply put it in the constructor of the service or controller and it should work "magically" without you actually need to make an instance of it.

Upvotes: 0

poke
poke

Reputation: 387825

Also, I am thinking, I might not be instantiating the SkipQContext rightly. As my statement:

SkipQContext db = new SkipQContext();

hits the default constructor of SkipQContext which is empty and not the overloaded constructor where IConfiguration is injected.

You are right, this is not how dependency injection is supposed to work. Whenever you do new Something, then you are explicitly going around dependency injection. The main point about dependency injection is that a component that has a dependency (e.g. a database context) does not need to create that dependency itself, or even know how to create that dependency itself.

When you call new SkipQContext(), you are explicitly creating that depenndency, so you are tightly coupled to that SkipQContext and whatever that context needs in order to work properly (in this case, it needs DbContextOptions and IConfiguration). What you want instead is components to be loosely coupled to their dependencies. So you just declare what dependencies you need and require that someone or something else fulfills these dependencies for you. And that’s exactly where dependency injection comes in:

With dependency injection, you have a “dependency injection container” which takes care of creating all the dependencies that you or some component may require. You configure the container in a central location, in Startup.ConfigureServices, and then you are able to simply declare what dependencies you need via a service’s constructor. But in order for the container to provide these dependencies to that service, the service will have to be created by the container itself.

So what you will see is that you will basically have to consume everything through dependency injection. But this also makes it easy to realize when you are not using dependency injection: Whenever you write new Something, then that something won’t be created by the container and as such won’t have its dependencies automatically fulfilled. Depending on what that something is that might be what you want, or maybe not (e.g. creating a List<string> or a DTO object is something you want to do directly, creating a service or something that has other dependencies likely isn’t).

Coming back to your problem, in order to have the DI container take care of the dependencies in the constructor of SkipQContext, you will have to let the DI container create that context for you. So you cannot create it with new but instead you will have to depend on it by adding it to the constructor of whatever component you need it in.

E.g. if you have a controller, just add it as a dependency there:

public class HomeController : Controller
{
    private readonly SkipQContext _db;

    public HomeController(SkipQContext db)
    {
        _db = db;
    }

    public async Task<IActionResult> Index()
    {
        var items = await _db.Items.ToListAsync();
        return View(new IndexViewModel
        {
            Items = items,
        });
    }
}

One final note regarding your database context: If you register the database context correctly with the DI container, then it will already be configured using the DbContextOptions that gets passed to the constructor. These options will also include the connection string the context needs to open the database connection. So you do not need to pass the IConfiguration manually or extract the connection string there. It will be automatically done for you by EF Core.

A proper context setup could look like this (in ConfigureServices):

services.AddDbContext<SkipQContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Upvotes: 1

Related Questions