Final Solution
With help from @NightOwl888\'s answer, here\'s the final approach I went with for anyone who ends up here:
1) Added the globa
You can build a custom filter provider (as in this answer) to control the lifetime of the filters, rather than registering them in the static GlobalFilters.Filters
collection.
public class GlobalFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var nestedContainer = StructuremapMvc.StructureMapDependencyScope.CurrentNestedContainer;
foreach (var filter in nestedContainer.GetAllInstances<IActionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in nestedContainer.GetAllInstances<IAuthorizationFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in nestedContainer.GetAllInstances<IExceptionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in nestedContainer.GetAllInstances<IResultFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in nestedContainer.GetAllInstances<IAuthenticationFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
}
}
Keep in mind, MVC already contains lots of filter types. Even implementations of the base Controller type are registered as global filters, as Controller
implements every type of filter. So, you need to be precise when you register your custom global filter types.
// Register the filter provider with MVC.
FilterProviders.Providers.Insert(0, new GlobalFilterProvider());
Then in your DI registration
Scan(_ =>
{
// Declare which assemblies to scan
// In this case, I am assuming that all of your custom
// filters are in the same assembly as the GlobalFilterProvider.
// So, you need to adjust this if necessary.
_.AssemblyContainingType<GlobalFilterProvider>();
// Optional: Filter out specific MVC filter types
_.Exclude(type => type.Name.EndsWith("Controller"));
// Add all filter types.
_.AddAllTypesOf<IActionFilter>();
_.AddAllTypesOf<IAuthorizationFilter>();
_.AddAllTypesOf<IExceptionFilter>();
_.AddAllTypesOf<IResultFilter>();
_.AddAllTypesOf<IAuthenticationFilter>(); // MVC 5 only
});
NOTE: You can control which filters MVC registers by changing the
IFilterProvider
instances that are registered.
So, an alternative could be something like:
FilterProviders.Providers.Clear();
// Your custom filter provider
FilterProviders.Providers.Add(new GlobalFilterProvider());
// This provider registers any filters in GlobalFilters.Filters
FilterProviders.Providers.Add(new System.Web.Mvc.GlobalFilterCollection());
// This provider registers any FilterAttribute types automatically (such as ActionFilterAttribute)
FilterProviders.Providers.Insert(new System.Web.Mvc.FilterAttributeFilterCollection());
Since the above code does not register the System.Web.Mvc.ControllerInstanceFilterProvider
, the Controllers themselves won't be registered as global filters, so you wouldn't need to filter them out. Instead, you could simply let all of your controllers be registered as global filters.
// Optional: Filter out specific MVC filter types
// _.Exclude(type => type.Name.EndsWith("Controller"));
// Register the filter provider with MVC.
FilterProviders.Providers.Insert(0, new GlobalFilterProvider());
Then in your DI registration
For<IActionFilter>().Use<MyGlobalActionFilter>();
For<IActionFilter>().Use<MyOtherGlobalActionFilter>();