TypeFilter vs ServiceFilter in ASP.NET Core MVC

TypeFilter vs ServiceFilter in ASP.NET Core MVC

In this article, I will discuss TypeFilter vs ServiceFilter, i.e., the Difference Between TypeFilter and ServiceFilter in ASP.NET Core MVC Applications with Examples. Please read our previous article discussing Action Filters in ASP.NET Core MVC Applications.

TypeFilter vs ServiceFilter in ASP.NET Core MVC

In ASP.NET Core, Filters execute custom pre- and post-processing logic before and after the action method execution. They can be used for various purposes, such as Authentication, Authorization, Caching, Exception Handling, Result Modification, etc. There are different kinds of filters (like Authorization, Action, Exception, and Result filters). We can also apply the filters to controllers and actions in different ways, such as applying at the action method level, at the controller level, or globally.

In ASP.NET Core, TypeFilter and ServiceFilter are built-in attributes that apply custom filters to controller actions or controllers. However, they have different purposes and are used in different scenarios. Now, let us proceed and try to understand the TypeFilter and ServiceFilter, what are the differences between them, and when to use which one:

TypeFilter Attribute in ASP.NET Core MVC:

TypeFilter is a Built-in Attribute in ASP.NET Core that allows us to apply a Custom Filter to a single action or controller. We must provide the filter type (Custom Filter Class name) as a parameter to the TypeFilter attribute. It is useful when we want to apply a Custom Filter Class to a specific action or controller and don’t need to configure it globally or inject additional services.

Example to Understand TypeFilter Attribute in ASP.NET Core MVC:

Let us see an example of understanding the TypeFilter Attribute in an ASP.NET Core MVC Application. Remember that you need to use the TypeFilter or Service Filter Attribute only when your Custom Filter class requires some services and you want to inject such services using the Dependency Injection Container.

So, let us first create a service that is going to be consumed by the Custom Filter Classes. So, create a class file named ILoggerService.cs and copy and paste the following code. Here, you can see that the ILoggerService interface defines one method called Log, and the LoggerService class implements the ILoggerService interface and provides the implementation for the Log method.

namespace FiltersDemo.Models
{
    public interface ILoggerService
    {
        public void Log(string methodName, string message);
    }

    public class LoggerService : ILoggerService
    {
        public void Log(string methodName, string message)
        {
            string filePath = Path.Combine(Directory.GetCurrentDirectory(), "Log", "Log.txt");

            //saving the data in a text file called Log.txt
            File.AppendAllText(filePath, message);
        }
    }
}
Creating Custom Action Filter:

Next, create a class file named CustomLoggingFilter.cs and then copy and paste the following code. As you can see, this class implements the IActionFilter interface and provides implementations for the OnActionExecuting and OnActionExecuted methods. Further, we have injected the LoggerService instance through constructor dependency injection.

using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersDemo.Models
{
    public class CustomLoggingFilter : IActionFilter
    {
        private readonly ILoggerService _LoggerService;

        public CustomLoggingFilter(ILoggerService LoggerService)
        {
            _LoggerService = LoggerService;
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var controllerName = context.RouteData.Values["controller"];
            var actionName = context.RouteData.Values["action"];
            string message = " Controller:" + controllerName + " Action:" + actionName + " Date: "
                            + DateTime.Now.ToString() + Environment.NewLine;

            // Log the information before the action executes.
            _LoggerService.Log("OnActionExecuting", message);
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            var controllerName = context.RouteData.Values["controller"];
            var actionName = context.RouteData.Values["action"];
            string message = " Controller:" + controllerName + " Action:" + actionName + " Date: "
                            + DateTime.Now.ToString() + Environment.NewLine;

            // Log the information after the action executes.
            _LoggerService.Log("OnActionExecuted", message);
        }
    }
}
Registering the Services to the Dependency Injection Container:

Next, we must register the Services to the Dependency Injection Container within the Program class so that the MVC Framework injects the service. So, add the following line to the Program class. Here, it is not required to register the Custom Filter into the dependency injection container.

builder.Services.AddScoped<ILoggerService, LoggerService>();

Apply Filter as a Service:

To apply the filter as a service, we can use TypeFilter Attribute. A TypeFilterAttribute is used to create an instance of a Custom Filter Class using dependency injection. You need to pass the Custom Filter Type as an argument to its constructor. The Type Filter instantiates the filter type each time the filter is applied. So, modify the Home Controller as follows:

using FiltersDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FiltersDemo.Controllers
{
    public class HomeController : Controller
    {
        [TypeFilter(typeof(CustomLoggingFilter))]
        public ActionResult Index()
        {
            return View();
        }

        [TypeFilter(typeof(CustomLoggingFilter))]
        public ActionResult Details()
        {
            return View();
        }
    }
}

Modify Index.cshtml File

@{
    ViewData["Title"] = "Index Page";
}

<h2>Index Page</h2>

Add Details.cshtml File

@{
    ViewData["Title"] = "Details Page";
}

<h2>Details Page</h2>
Testing the Application:

Each time a request is made to either the Index or Details action methods, a new instance of CustomLoggingFilter is created. This is because the [TypeFilter] attribute creates an instance of the specified filter type per request for each action method it decorates.

Therefore, if we make a request to the Index method and another request to the Details method, two separate instances of CustomLoggingFilter will be created, i.e., one for each request. Each subsequent request to these methods will create new instances accordingly. Now, run the application, access both Index and Details action methods, and then check the Log.txt file; you should see that the Instance of the CustomLoggingFilter was created twice, as shown in the image below.

Example to Understand TypeFilter Attribute in ASP.NET Core MVC

ServiceFilter Attribute in ASP.NET Core MVC:

ServiceFilter is also a Built-in Attribute in ASP.NET Core that applies a Custom Filter as an Attribute in the Controller or Controller Action Method. However, the Custom Filter must be registered as a service in the dependency injection container. It is useful when we want to reuse the same Custom Filter Instance across multiple actions or controllers. That means, unlike the TypeFilter, it doesn’t create a new instance but retrieves it from the DI container.

We are going to work with the same example. So, modify the Home Controller as follows to use the ServiceFilter Attribute. In both action methods, you can see that we have applied the CustomLoggingFilter using the ServiceFilter attribute.

using FiltersDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FiltersDemo.Controllers
{
    public class HomeController : Controller
    {
        [ServiceFilter(typeof(CustomLoggingFilter))]
        public ActionResult Index()
        {
            return View();
        }

        [ServiceFilter(typeof(CustomLoggingFilter))]
        public ActionResult Details()
        {
            return View();
        }
    }
}

At this point, if you run the application, then you will get the following 500 HTTP Error. This is because we are applying the CustomLoggingFilter using the ServiceFilter attribute, but we have not yet registered the CustomLoggingFilter as a service into the built-in dependency injection container.

ServiceFilter Attribute in ASP.NET Core MVC

Registering the Custom Filter as a Service:

With TypeFilter, we specify the filter type directly within the TypeFilter constructor and don’t need to register it as a service into the Dependency Injection Container. But, with ServiceFilter, we must register the Custom Filter as a service into the Dependency Injection Container before using it with the ServiceFilter attribute. So, add the following code to the Program class.

builder.Services.AddSingleton<CustomLoggingFilter>();

With the above changes, run the application and access both the Index and Details action methods. Then verify the Log.txt file, and this time, you will see that only one instance of the CustomLoggingFilter is created, as shown in the image below. This is because we registered the CustomLoggingFilter using the AddSingleton method, which, as we already discussed, will create only one instance of the registered service throughout the application’s life and rescue that instance.

Differences Between TypeFilter and ServiceFilter in ASP.NET Core

Differences Between TypeFilter and ServiceFilter in ASP.NET Core:

The following are the Differences Between TypeFilter and ServiceFilter in ASP.NET Core MVC:

  • TypeFilter creates a new instance of the filter type on each request. On the other hand, the ServiceFilter retrieves the instance from the DI container, which could be a singleton, scoped, or transient instance, depending on how it’s registered.
  • The TypeFilter is more flexible because it can instantiate types that aren’t registered in the DI container, while the ServiceFilter requires the type to be registered in the DI container.

So, we need to use TypeFilter if we need to inject dependencies that aren’t registered in the container. We need to use ServiceFilter if the filter is already registered as a service in the container, and we want the Dependency Injection Container to manage the filter’s lifetime.

In the next article, I will discuss Cross-Site Request Forgery and Antiforgery Tokens in ASP.NET Core MVC. In this article, I explain the difference between TypeFilter and ServiceFilter in ASP.NET Core Applications with examples. I hope you enjoy this Difference Between TypeFilter and ServiceFilter in the ASP.NET Core article.

Leave a Reply

Your email address will not be published. Required fields are marked *