Get DbContext in async action

1.6k views Asked by At

I am rewriting/ moving a website from ASP MVC to ASP MVC Core. This application has a dynamic menu which depends on the logged in user. In order to build the menu, each controller derives from a custom BaseController who sets in ViewBag menu items, an later, in Layout, those items are retrieved and passed as arguments to a PartialView.

public BaseController:Controller
{
    public BaseController()
    {
          ...
          ViewBag.Menu=Utils.GetMenu();
          ...
    }
}

I don't want to use the same logic as the lads who wrote the old code. So I thought to use a ViewComponent to render the menu. But I have a problem with this approach. In Invoke method I need to query for the menu items. Right now I get a DbContext instance from the service provider (HttpContext.RequestServices), and I use it to query whatever data I need. But the Invoke function is called asynchronously from Layout and I know that it is not very good to send DbContext to async methods:

<cache expires-after="@TimeSpan.FromHours(2)" enabled="true">
       @await Component.InvokeAsync(typeof(Admin.ViewComponents.MeniuViewComponent))
</cache>

Is this a good approach? Is it safe to get a DbContext (registered as Scoped in Startup) in Invoke method (or any other async method or action) and use it? And if it is not a good idea, how should I deal with this kind of situations where I need data from db in async methods?

2

There are 2 answers

4
Danny Schneider On BEST ANSWER

I had the case in my project to get the DbContext inside a HostedService from asp.net core.

I injected the IServiceProvider inside the constructor and build my own scope, to get the registered DbContext:

private readonly IServiceProvider _serviceProvider;
public BaseController(IServiceProvider provider){
  _serviceProvider = provider;
}

private void DoSth(){
  using var scope = _serviceProvider.CreateScope();
  var db = scope.ServiceProvider.GetRequiredService<YOUR_DbContext_CLASS>();

  ... //do something great stuff here with the database
}
1
mj1313 On

I think you can directly dependency inject the DbContext in the ViewComponent

public class MenuViewComponent : ViewComponent
{
    private readonly MenuDbContext _context;

    public TopMenuViewComponent(MenuDbContext context)
    {
        _context = context;

    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var model = _context.MenuItems.ToList();

        return View("Default", model);
    }
}

For more details, refer to the doc.