How to disable a global filter in ASP.Net MVC selectively

asp.net MvcActionfilterattributeGlobal Filter

asp.net Mvc Problem Overview


I have set up a global filter for all my controller actions in which I open and close NHibernate sessions. 95% of these action need some database access, but 5% don't. Is there any easy way to disable this global filter for those 5%. I could go the other way round and decorate only the actions that need the database, but that would be far more work.

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

You could write a marker attribute:

public class SkipMyGlobalActionFilterAttribute : Attribute
{
}

and then in your global action filter test for the presence of this marker on the action:

public class MyGlobalActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipMyGlobalActionFilterAttribute), false).Any())
        {
            return;
        }

        // here do whatever you were intending to do
    }
}

and then if you want to exclude some action from the global filter simply decorate it with the marker attribute:

[SkipMyGlobalActionFilter]
public ActionResult Index()
{
    return View();
}

Solution 2 - asp.net Mvc

Though, the accepted answer by Darin Dimitrov is fine and working well but, for me, the simplest and most efficient answer found here.

You just need to add a boolean property to your attribute and check against it, just before your logic begins:

public class DataAccessAttribute: ActionFilterAttribute
{
    public bool Disable { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (Disable) return;

        // Your original logic for your 95% actions goes here.
    }
}

Then at your 5% actions just use it like this:

[DataAccessAttribute(Disable=true)]
public ActionResult Index()
{            
    return View();
}

Solution 3 - asp.net Mvc

In AspNetCore, the accepted answer by @darin-dimitrov can be adapted to work as follows:

First, implement IFilterMetadata on the marker attribute:

public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}

Then search the Filters property for this attribute on the ActionExecutingContext:

public class MyGlobalActionFilter : IActionFilter
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.Filters.OfType<SkipMyGlobalActionFilterAttribute>().Any())
        {
            return;
        }

        // etc
    }
}

Solution 4 - asp.net Mvc

At least nowadays, this is quite easy: to exclude all action filters from an action, just add the OverrideActionFiltersAttribute.

There are similar attributes for other filters: OverrideAuthenticationAttribute, OverrideAuthorizationAttribute and OverrideExceptionAttribute.

See also https://www.strathweb.com/2013/06/overriding-filters-in-asp-net-web-api-vnext/

Solution 5 - asp.net Mvc

Create a custom Filter Provider. Write a class which will implement IFilterProvider. This IFilterProvider interface has a method GetFilters which returns Filters which needs to be executed.

public class MyFilterProvider : IFilterProvider
{
        private readonly List<Func<ControllerContext, object>> filterconditions = new List<Func<ControllerContext, object>>();
        public void Add(Func<ControllerContext, object> mycondition)
        {
            filterconditions.Add(mycondition);
        }
 
        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return from filtercondition in filterconditions
                   select filtercondition(controllerContext) into ctrlContext
                   where ctrlContext!= null
                   select new Filter(ctrlContext, FilterScope.Global);
        }
}

=============================================================================
In Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            MyFilterProvider provider = new MyFilterProvider();
            provider.Add(d => d.RouteData.Values["action"].ToString() != "SkipFilterAction1 " ? new NHibernateActionFilter() : null);
            FilterProviders.Providers.Add(provider);
        }


protected void Application_Start()
{
    RegisterGlobalFilters(GlobalFilters.Filters);
}

Solution 6 - asp.net Mvc

Well, I think I got it working for ASP.NET Core.
Here's the code:

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Prepare the audit
        _parameters = context.ActionArguments;

        await next();

        if (IsExcluded(context))
        {
            return;
        }
        
        var routeData = context.RouteData;

        var controllerName = (string)routeData.Values["controller"];
        var actionName = (string)routeData.Values["action"];

        // Log action data
        var auditEntry = new AuditEntry
        {
            ActionName = actionName,
            EntityType = controllerName,
            EntityID = GetEntityId(),
            PerformedAt = DateTime.Now,
            PersonID = context.HttpContext.Session.GetCurrentUser()?.PersonId.ToString()
        };

        _auditHandler.DbContext.Audits.Add(auditEntry);
        await _auditHandler.DbContext.SaveChangesAsync();
    }

    private bool IsExcluded(ActionContext context)
    {
        var controllerActionDescriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;

        return controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(ExcludeFromAuditing), false) ||
               controllerActionDescriptor.MethodInfo.IsDefined(typeof(ExcludeFromAuditing), false);
    }

The relevant code is in the 'IsExcluded' method.

Solution 7 - asp.net Mvc

You can change your filter code like this:

 public class NHibernateActionFilter : ActionFilterAttribute
    {
        public IEnumerable<string> ActionsToSkip { get; set; }
    
        public NHibernateActionFilter(params string[] actionsToSkip)
        {
            ActionsToSkip = actionsToSkip;
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (null != ActionsToSkip && ActionsToSkip.Any(a => 
String.Compare(a,  filterContext.ActionDescriptor.ActionName, true) == 0))
                {
                    return;
                }
           //here you code
        }
    }

And use it:

[NHibernateActionFilter(new[] { "SkipFilterAction1 ", "Action2"})]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzszepView Question on Stackoverflow
Solution 1 - asp.net MvcDarin DimitrovView Answer on Stackoverflow
Solution 2 - asp.net MvcDr. MAFView Answer on Stackoverflow
Solution 3 - asp.net Mvcg tView Answer on Stackoverflow
Solution 4 - asp.net MvcMarkus WagnerView Answer on Stackoverflow
Solution 5 - asp.net MvcSwapnilView Answer on Stackoverflow
Solution 6 - asp.net MvcashilonView Answer on Stackoverflow
Solution 7 - asp.net MvcNatalyaView Answer on Stackoverflow