0
votes

Using ASP.NET Core 5.0, I'm attempting to access IWebHostEnvironment in an EF Core 5.0 model class. How can I get access to the check if IsDevelopment is true from the environment?

I'm calling the below class from my Controllers like so:

private MyContext db = new();

Do I really need to also spin up IWebHostEnvironment in each controller that calls this EF class to target the correct constructor?

public partial class MyClass : DbContext
{
    private readonly IWebHostEnvironment env;

    public MyContext()
    {
    }

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

    public MyContext(IWebHostEnvironment _env)
    {
        this.env = _env;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
         if(this.env.IsDevelopment()) { ... } // env is null
    }
}

I've seen a few threats about DI, but to be honest, they have convoluted solutions that I can't quite grok.

EDIT with new code:

public IConfiguration Configuration { get; }
public IWebHostEnvironment WebHostEnvironment { get; }

public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
    WebHostEnvironment = webHostEnvironment;
    Configuration = configuration;
}

public void ConfigureServices(IServiceCollection services)
{
    DbContextOptionsBuilder<MyContext> builder = new DbContextOptionsBuilder<MyContext>();
    builder.UseSqlServer(@"Server=localhost;[...]");
    services.AddScoped<MyContext>(_ => new MyContext(builder.Options, WebHostEnvironment));
    services.AddDbContext<MyContext>();

EF context:


public partial class MyContext : DbContext
{
    private readonly IWebHostEnvironment Env;

    public MyContext()
    {
    }

    public MyContext(DbContextOptions<MyContext> options, IWebHostEnvironment env)
        : base(options)
    {
        Env = env;
    }
}

Controller:

public class MyController : Controller
{
    private  MyContext db;
    public ActionResult Index(MyContext db)
    {
        this.db = db; //throws exception below
        return View();
    }
}

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. Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)

1
DI only works for objects that are provided to you by the framework (i.e. registered in StartUp). If you are manually instantiating the DbContext then, yes, you need to pass the IWebHostEnvironment from your controller. Your other option is to register the DbContext itself in the DI container and then request the DbContext in the constructor of your Controller so that you then have that DbContext injected into your Controller already loaded up with the IWebHostEnvironment.Neil W
@NeilW that would be perfect. I would assume that if I did this in the DI container (any handy examples I can mark as an answer?), I would remove the OnConfiguring override, correct?Trevor Seward
See posted answer. Nope, keep your OnConfiguring. It can now use the IWebHostEnvironment that injected. Hope the answer clarifies.Neil W
Give me a couple of mins and I'll update the answer so you can provide the Options as well.Neil W
Yup, that should do it. Now, you'll have the DbContext (with Options set and WebHostEnv available) injected into your Controller. You should be able to play with that answer to suit your specific needs.Neil W

1 Answers

0
votes

Your DbContext

public class MyContext : DbContext
{
    private readonly IWebHostEnvironment WebHostEnv;
    public MyContext(
        DbContextOptions<MyContext> options, 
        IWebHostEnvironment webHostEnv) : base(options)
    {
        WebHostEnv = webHostEnv;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
         if(this.WebHostEnv.IsDevelopment()) { ... } // env is null
    }

}

In StartUp add

public IConfiguration Configuration { get; }
public IWebHostEnvironment WebHostEnvironment { get; }

public StartUp(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
    Configuration = configuration;
    WebHostEnvironment = webHostEnvironment;
}

public void ConfigureServices(IServiceCollection services)
{
   // ... some services

   DbContextOptionsBuilder<MyContext> optsBuilder = new DbContextOptionsBuilder<MyContext>();
   optsBuilder.UseSqlServer(Configuration.GetConnectionString("connstringname"));
   services.AddScoped<MyContext>(_ => 
       new MyContext(optsBuilder.Options, WebHostEnvironment));
}

Then your controller

public class MyController
{
    // MyContext already loaded with IWebHostEnvironment
    private readonly MyContext MyLoadedContext;

    public MyController(MyContext myLoadedContext)
    {
        MyLoadedContext = myLoadedContext;
    }
}