Search code examples
c#entity-frameworkasp.net-coreasp.net-core-webapief-power-tools

.NET 5 properly injecting DbContext connection string when using EF Core Power Tools


Hoping for guidance on best practices for setting a connection string in a .NET 5 Web API. I am using EF Core Power Tools to reverse engineer a database model and I have a connection string in an appsettings.json file. Trying to figure out how I should apply the steps from Microsoft's DbContext configuration documentation with my existing setup.

Project setup (shortened for brevity) basically looks like this:

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "..."
  }
}

Startup.cs

namespace API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(AppContext.BaseDirectory)
                .AddJsonFile("appsettings.json", false, true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));

            Globals.Configuration = Configuration;
        }
    }
}    

ApplicationDbContext.cs (autogenerated by EF Core Power Tools)

namespace API.Data
{
    public partial class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext()
        {
        }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
        
        ...
    }
}

ExampleController.cs

namespace API.Controllers
{
    [ApiController]
    [Route("example")]
    public class ExampleController : ControllerBase
    {
        [HttpGet("test")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public ActionResult<ApiResponse> GetExample()
        {
            using (var db = new ApplicationDbContext())
            {
                ...
            }
        }
    }
}

Now, that setup alone does not work (example controller would result in a "No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method..." error).

I have been working around this by adding a Connection String property inside the ApplicationDbContext.cs class (after it autogenerates) and setting the property within ConfigureServices inside Startup.cs like this:

ApplicationDbContext.ConnectionString = Configuration.GetConnectionString("DefaultConnection");

That does allow the use of using(var db = new ApplicationDbContext() { } throughout the application, but this connection string setup gets overwritten every time I refresh the model with EF Core Power Tools.

Is there a better example to follow, or what's the best approach here? Thanks!


Solution

  • First of All, as you have configured DB Connection as a option. so there is no need for default constructor of ApplicationDbContext

    public partial class ApplicationDbContext : DbContext
        {
            
            public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
                : base(options)
            {
            }
            
        }
    

    this should be fine, if you want to set connection string from startup

    Now as you have already injecting ApplicationDbContext in the startup, you can easily use on controller as a dependency injection rather than creating an new object. because creating new object you'll need to set connection string on that AppDbContext and which is not a good approach!

        [ApiController]
        [Route("example")]
        public class ExampleController : ControllerBase
        {
            private readonly ApplicationDbContext _applicationDbContext;
    
            ExampleController (private ApplicationDbContext applicationDbContext) {
                _applicationDbContext = applicationDbContext;
           }
    
    
            [HttpGet("test")]
            [ProducesResponseType(StatusCodes.Status200OK)]
            [ProducesResponseType(StatusCodes.Status404NotFound)]
            public ActionResult<ApiResponse> GetExample()
            {
                //use db context 
                var something = _applicationDbContext; 
            }
        }