Passive Attributes and Nested Containers

前端 未结 1 942
攒了一身酷
攒了一身酷 2020-12-22 10:38

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

相关标签:
1条回答
  • 2020-12-22 11:36

    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);
            }
        }
    }
    

    Usage

    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.

    Option 1: Using convention based registration

    // 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"));
    

    Option 2: Individual registration

    // 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>();
    
    0 讨论(0)
提交回复
热议问题