How to implement a custom base class for razor views in ASP.NET Core
asp.net-core razor-library razor

How to implement a custom base class for razor views in ASP.NET Core

Not well known practice and not well documented but quite powerful: it is possible to override the base class of the views and pages in order to share a same logic or just to simplify some calls within views.

How to create a custom base class for Razor Views?

Customizing a view class is simple, it just requires to change the base class of the view with a type extending Microsoft.AspNetCore.Mvc.Razor<TModel>. Your new class should be abstract and look something like this:

public abstract class BasePage<TModel> : RazorPage<TModel>
{
    protected BasePage()
    {
    }
}

From there you can add almost any behavior, functions or properties you like.

How to use a custom base class for Razor Views?

Before going any further, how to use this new toy? Easy, just reference it as follow in any view:

@inherits MD.Bases.BasePage<TModel>

You may also write this line at the top of _ViewImports.cshtml to apply it to all views of the project!

What can I do with custom base class for razor views?

Dependency Injection using attribute

One of the cool features of the razor engine is the dependency injection by attribute:

public abstract class BasePage<TModel> : RazorPage<TModel>
{
    [RazorInject]
    public IServiceProvider ServiceProvider { get; set; }
    
    protected BasePage()
    {
    }
}

This will be populated at class instanciation by the razor engine.

Why would you customize the base class of your views?

Here are few examples:

// Returns the file path of the view
public string ViewPath => ViewContext.ExecutingFilePath;
// Returns the file name of the view
public string ViewFileName => System.IO.Path.GetFileName(ViewContext.ExecutingFilePath);

// Standardized way to fill the title and description,
// avoiding any misspell and factorized way to do the action
public void SetTitle(string title) { ViewData["Title"] = title; }
public void SetDescription(string descr) { ViewData["Description"] = descr; }

// Allows to easily get a query parameter
public StringValues GetQueryParameter(string key)
            => Context.Request.GetQueryParameter(key);

// Returns the site address
public string SiteBasePath { get { return $"{Context.Request.Scheme}://{Context.Request.Host.Host}{(Context.Request.Host.Port != 80 && Context.Request.Host.Port != 443 ? $":{Context.Request.Host.Port}" : "")}"; } }

public override void BeginContext(int position, int length, bool isLiteral)
{
    // Do some work here will be executed before every part of contexts of the view. 
    // Each code block of the view, taghelper or html block will act as a context.
    base.BeginContext(position, length, isLiteral);
}

Which can be used as follow in a view:

@inherits MD.Bases.MDBasePage<TModel>
@model Dictionary<string, bool>
@{
    SetTitle("Test page");
    SetDescription("This is a page to test the implementation of a base model");
    var myParam = GetQueryParameter("MyParam").FirstOrDefault();
}

The site address is '@(SiteBasePath)'.

You called the view '@ViewContext.ExecutingFilePath'
    
@if (myParam != null)
{
    <text>myParam query parameter is defined with the value '@myParam'</text>
}
else
{
    <text>myParam query parameter is not defined</text>
}

In this post we have seen how to implement a custom base class for ASP.NET Core Razor views, and few use-cases. Let a comment to tell what you use this kind of implementation for. In the next post, we will explain how to use this feature to implement a nice translation system!


Send
Please sign-in to comment
.X0001-01-01_00-00