9

In the new ASP.NET Core MVC, it seems the standard way to get the context to the controller is by doing this

public class BaseController : Controller
{
    public readonly ReportDBContext _db;
    
    public BaseController(ReportDBContext db)
    {
        _db = db;
    }
}

I can then use

public class HomeController : BaseController
{   
    public HomeController(ReportDBContext db) : base(db) { }
}

To make this a little easier in all other controllers.

Normally in ASP.NET MVC, I can get a context any time by using new ReportDBContext()

Is there a similar way of this now, or do I have to have the context in all controllers in ASP.NET Core MVC?

2
  • 1
    Seems like you don't understand the benefits of Dependency Injection. DI is incredibly important to writing software that is flexible and easy to maintain. There's really not a good argument to not use it. I suggest you watch this video to obtain an understanding of DI and the benefits. Commented Jun 16, 2020 at 15:18
  • Highly recommended article from the official docs before trying to get deeper into DbContext: learn.microsoft.com/en-us/ef/core/dbcontext-configuration Commented Jan 31 at 16:33

4 Answers 4

13

Thanks to @mm8's answer, if you decided to use dependency injection, then you can follow the below mentioned steps.

Suppose that you have defined your ReportDBContext like this:

public class ReportDBContext : DbContext
{
    public DbSet<Sample> Samples { get; set; }
    //...

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

So you need to config your startup.cs like this:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //...What needed
        services.AddDbContext<ReportDBContext>(options => options.UseSqlServer("Connection string to your DB"));
        //...What needed
    }
    //...
 }

So you can easily inject your ReportDBContext to your classes like this(for example inject it to one of your controllers):

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly ReportDBContext _dbContext;

    public ValuesController(ReportDBContext dbContext )
    {
        _dbContext = dbContext;
    }
    //...
}

You can simply inject the ReportDBContext to your BaseController instead of injecting it to every controller in your project too.

Update 1

If you do not want to inject the ReportDBContext into each constructor, then you can design your BaseController as follow by using HttpContext.RequestServices:

public class BaseController : Controller
{
    protected ReportDBContext DbContext => (ReportDBContext)HttpContext.RequestServices.GetService(typeof(ReportDBContext));
    //...
}

[Route("api/[controller]")]
public class ValuesController : BaseController
{
    //...

    [HttpGet]
    public List<Sample> Get() => DbContext.Samples.ToList();
}

Read more here:

  1. https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
  2. http://www.binaryintellect.net/articles/17ee0ba2-99bb-47f0-ab18-f4fc32f476f8.asp

You will need to install at least these two NuGet packages too:

  1. Microsoft.EntityFrameworkCore
  2. Microsoft.EntityFrameworkCore.SqlServer (If the database provider is Microsoft SQL Server)
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, that's pretty much what I've got now - I just wanted to know if there was a better way of doing it. It just seems like your going to end up with 20 or so controllers each with a contructor that takes the DBContext.
@RichardHousham I have said at the end of my answer that you can inject your dbContext to your BaseController instead of injecting it to each of the controllers :-)
Yeah, but that would still require your normal controller to still have it in the constructor ie. public HomeController(ReportDBContext db) : base(db) { }
Or is there another way?
@RichardHousham I have updated the answer which now covers your requirements
|
3

Normally in Asp.net MVC I can get a context any time by using new ReportDBContext()...

You could do the same, i.e. create a new context explicitly in the controller, nowadays. There is nothing that stops for you from doing this, except for best practices.

Some of the benfits of using dependency injection is that you can define the lifetime of the dependency in the Startup class and reuse it across all your controllers, and that you can mock away dependencies in your unit tests. Please refer to the docs and this question for more information.

But you certainly don't have to use dependency injection if you don't want to or have a reason not to do so. The compiler won't force to you define a custom constructor that accepts a dependency.

1 Comment

This does not always work in all scenarios and may require you to hard code the connection string to the database. It may work fine for running your application, but not when doing a migration. The issue should be fixed in .Net7.
2

The architecture of ASP.NET Core is built on Dependency Injection. It even has a built-in dependency injection container, but you can also user others like AutoFac or NInject. If you need the DbContext in more than one action, you can use constructor injection, like in your example.

If you need it only once, you can inject it directly into the action method like so:

public IActionResult Get([FromServices]ReportDbContext db) 
{ 
   …
} 

The usage of Dependency Injection makes your code more testable. For your unit tests you could inject a InMemory DbContext.

In my opinion it is even less work than constructing the DbContext yourself. It's done by the framework.

2 Comments

That's pretty interesting, if I have 2 contexts but maybe only use 1 in a handful of places could I use this then?
@RichardHousham Yes, that should work. As long as both contexts are registered
0

Another approach is creating a "ServiceManager" class have various items in there like for instance I've got classes for database context, Active Directory and the IConfiguration for the config file.

Add in a creator like this...

public class ServiceManager
{
    public DBConn DBConn { get; set; }
    public iADLookup iADLookup { get; set; }

    public IConfiguration configuration { get; set; }

    public ServiceManager(DBConn conn, iADLookup aDLookup, IConfiguration config)
    {

        DBConn = conn;
        iADLookup = aDLookup;
        configuration = config;
    }
}

and into the program like this.

builder.Services.AddDbContext<DBConn>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DBConn")));

builder.Services.AddScoped<iADLookup, ADLookup>();

//create a service manager 
builder.Services.AddTransient<ServiceManager>();

Use in your controllers like this..

public readonly DBConn _db;
public readonly iADLookup _ad;

public readonly IConfiguration _config;


public BaseController(ServiceManager serviceManager)
{
    _db = serviceManager.DBConn;
    _ad = serviceManager.iADLookup;
    _config = serviceManager.configuration;
}

This works for me and allows you to add in more "services" easily.

1 Comment

I don't think this is essential to the question. It's just an arbitrary way of organising DI. Many users could post similar answers, which, in the end, only adds noise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.