Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render a Partial View in _layout.cshtml using the partialview model instance with DBContext dependency injection

At first I'd like to say I'm new to Asp.net Core and also with concepts of dependency injection (DI). I'm reading a lot trying understand it, so I ask for patience.

I'm trying Razor Pages (not MVC) and my goal is to render to "_layout.cshtml" a partial view, in which information obtained using the Entity Framework is available on all pages.

I added the DBContext in the Startup.cs file as follows:

  services.AddDbContext <AppDBContext> (options =>
  options.UseSqlServer (Configuration.GetConnectionString ("AppDBContext")

And in "PageModel" for MenuPartial.cshtml.cs

   

 public class MenuPartialModel: PageModel
    {
       
       private readonly AppDBContext _db;
      

        public MenuPartialModel (AppDBContext db)      
        {
            _db = db;
        }
                 
}

In the _layout.cshtm file I tried several ways to call PartialAsync with the instance of a new model:

If set new instance model like bellow I'll be using constructor without parameters, therefore the DbContext is not injected

@await Html.PartialAsync ("MenuPartial", new MenuPartialModel ())

And I also thought about using:

@await Html.PartialAsync ("MenuPartial", new MenuPartialModel (new AppDBContext ())

I believe that wouldn't be a correct approach. Because if the DbContext was injected automatically, why should I perform a new instance by passing connection parameters again?

What would be the best approach to achieve my goal? I thought about using ViewComponents, however, at first I would like to understand if there is any way to instantiate a model and use the constructor that injects the DBContext.

like image 909
Marcoscdoni Avatar asked Aug 25 '18 03:08

Marcoscdoni


1 Answers

I would like to understand if there is any way to instantiate a model and use the constructor that injects the DBContext.

In ASP.NET Core MVC, plain old CLR classes (POCOs) do not participate in ASP.NET Core Dependency Injection. The MenuPartialModel is a POCO (as are most MVC models). As a result, the framework will not automatically inject dependencies into it.

Some built-in ASP.NET Core MVC classes are wired into the dependency injection. These include (but are not limited to):

  • Controllers,
  • Tag Helpers,
  • Razor Views,
  • Razor Pages, and
  • View Components.

The canonical approach in MVC is to inject dependencies (aka services) into one of those, and then to pass any data manually to the POCO model.

What would be [a good] approach to achieve my goal?

Instead of Partial View, I would use a View Component. A view component is like a partial view that has a model, a view, and a controller.

The controller is InvokeAsync, which isn't exposed to HTTP, but it is still a controller in the traditional sense, because it is responsible for binding the model to the view.

Basic View Component with Dependency Injection

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MyApplication.Data;

public class MenuModel
{
    public string MyProperty { get; set; }
    public string MyOtherProperty { get; set; }
}

public class MenuViewComponent : ViewComponent
{
    private readonly ApplicationDbContext dbContext;

    public MenuViewComponent(ApplicationDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var data = await dbContext...

        var model = new MenuModel 
        {
            MyProperty = data.FirstValue,
            MyOtherProperty = data.OtherValue,
        };

        return View(model);
    }
}

You will also need a Razor file at one of these locations to act as the view.

/Pages/Shared/Components/Menu/Default.cshtml
/Views/Shared/Components/Menu/Default.cshtml

That Default.cshtml file will contain something like this:

@model MenuModel

<p>@Model.MyProperty</p>
<p>@Model.MyOtherProperty</p>

Using the View Component

@await Component.InvokeAsync("Menu")

Accessing the View Component with AJAX

Out of the box, View Components are not exposed to HTTP. To access them via AJAX, we need to add an ASP.NET Core MVC Controller to reach them.

[Route("component/[action]")]
public class ViewComponentController : Controller 
{ 
    // ~/component/menu
    public IActionResult Menu() => ViewComponent("Menu");
}
like image 160
Shaun Luttin Avatar answered Oct 20 '22 14:10

Shaun Luttin