defect833
defect833

Reputation: 285

Using ApplicationDbContext with DI from appsettings.json

I am trying to abstract any connection information away from my ApplicationDbContext class so that I can take advantage of different databases for development, staging, production. I start by registering a service from Startup.cs

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

My ApplicationDbContext class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
     : base(options)
   {
   }

    protected override void OnModelCreating(ModelBuilder builder)
    {
         base.OnModelCreating(builder);
    }
}

When running this application I get the following error:

InvalidOperationException: Could not create an instance of type 'SquadApps.Data.ApplicationDbContext'. Model bound complex types must not be abstract or value types and must have a parameterless constructor.

So naturally I tried adding a parameterless constructor

public ApplicationDbContext() { } 

Now getting another error:

InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.

If I go back to having a connection string stored in the ApplicationDbContext class like so:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer("........");
}

Then everything works fine but obviously this is not ideal and probably a bad practice. I think there is something i'm missing about the DI process and any suggestions or advice would be appreciated.

Upvotes: 3

Views: 1302

Answers (1)

defect833
defect833

Reputation: 285

The solution turned out to be how I was trying to call the DI. I had incorrectly assumed DI would be able to be called per each IActionResult inside my controllers but in fact it must occur within the constructor of the controller. This makes the DI available to all IActionResult methods within the controller.

Example of the working DI call:

public class HomeController : Controller
{
    private readonly ApplicationDbContext _ctx;
    private readonly CompanySettings _companySettings;

    public HomeController(ApplicationDbContext ctx, IOptions<CompanySettings> settings)
    {
        _ctx = ctx;
        _companySettings = settings.Value;
    }

    public IActionResult Index()
    {
        var model = new HomeViewModel();

        // _ctx and _companySettings can be used here

        return View(model);
    }
}

Upvotes: 1

Related Questions