被动属性和嵌套容器

最终解决方案

在@ NightOwl888的答案的帮助下,这是我最后的方法,对于任何最终到此的人:

1)添加了全局filter提供程序:

public class GlobalFilterProvider : IFilterProvider { public IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var nestedContainer = StructuremapMvc.StructureMapDependencyScope.CurrentNestedContainer; foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } } } 

2)在FilterProviders集合中注册它:

 public static void Application_Start() { // other bootstrapping code... FilterProviders.Providers.Insert(0, new GlobalFilterProvider()); } 

3)使用被动属性方法添加了自定义filter:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class SomeAttribute : Attribute { } public class SomeFilter : IActionFilter { private readonly ISomeDependency _dependency; public SomeFilter(ISomeDependency dependency) { _dependency = dependency; } public void OnActionExecuting(ActionExecutingContext filterContext) { if (!filterContext.ActionDescriptor.GetCustomAttributes(true).OfType().Any()) return; _dependency.DoWork(); } public void OnActionExecuted(ActionExecutedContext filterContext) { } } 

4)然后在StructureMap中连接所有内容(在此解决方案中,SomeAttribute和GlobalFilterProvider类位于根文件夹中的相同“Filters”文件夹中):

 public class ActionFilterRegistry : Registry { public ActionFilterRegistry() { Scan(s => { // find assembly containing custom filters s.AssemblyContainingType(); // limit it to the folder containing custom filters s.IncludeNamespaceContainingType(); // exclude any of the Attribute classes that contain metadata but not the behavior s.Exclude(type => type.IsSubclassOf(typeof(Attribute))); // register our custom filters s.AddAllTypesOf(); }); } } 

原帖

我目前在ASP.NET MVC 5应用程序中使用StructureMap的每个请求使用嵌套容器。 我正在利用structuremap.mvc5 nuget包为我设置所有DI基础设施(依赖解析器,连接容器以及在App_BeginRequestApp_EndRequest上创建和处理嵌套容器)。 我现在正处于需要在动作filter中执行某些DI以便自动执行某些function的位置。 经过大量的研究,我试图在不需要二次注射的情况下使用Mark Seemann的被动属性方法。

在构建属性和filter时,一切似乎都很好,直到我在App_Start中使用全局filter集合注册filter。 我有一个依赖项,我希望每个请求只创建一次,这样不仅动作filter,而且在请求期间使用的其他非filter基础结构类,可以在整个请求中使用该依赖项的相同实例。 如果嵌套容器正在解析依赖项,则默认情况下会这样做。 但是,因为我必须在App_Start中注册新filter,所以我无权访问嵌套容器。

例如,我的global.asax:

 public class MvcApplication : System.Web.HttpApplication { public static StructureMapDependencyScope StructureMapDependencyScope { get; set; } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); var container = IoC.Initialize(); // contains all DI registrations StructureMapDependencyScope = new StructureMapDependencyScope(container); DependencyResolver.SetResolver(StructureMapDependencyScope); // filter uses constructor injection, so I have to give it an instance in order to new it up, // but nested container is not available GlobalFilters.Filters.Add(new SomeFilter(container.GetInstance())); } protected void Application_BeginRequest() { StructureMapDependencyScope.CreateNestedContainer(); } protected void Application_EndRequest() { HttpContextLifecycle.DisposeAndClearAll(); StructureMapDependencyScope.DisposeNestedContainer(); } protected void Application_End() { StructureMapDependencyScope.Dispose(); } } 

有谁知道如何解决这个问题? 我也通过其他SO问题遇到了decoraptor解决方案,但在我的filter中使用抽象工厂只会创建一个新的依赖实例,而不是使用我想要的单个请求实例。

我提出的唯一其他解决方案是使用setter注入和自定义filter提供程序,该提供程序使用在全局中创建的静态StructureMapDependencyScope实例,如下所示:

 public class StructureMapFilterProvider : FilterAttributeFilterProvider { public override IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var filters = base.GetFilters(controllerContext, actionDescriptor); foreach (var filter in filters) { MvcApplication.StructureMapDependencyScope.CurrentNestedContainer.BuildUp(filter.Instance); } return filters; } } 

虽然这似乎工作正常,但它似乎有点脏。

您可以构建自定义filter提供程序(如本答案中所述 )来控制filter的生命周期,而不是在静态GlobalFilters.Filters集合中注册它们。

 public class GlobalFilterProvider : IFilterProvider { public IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { var nestedContainer = StructuremapMvc.StructureMapDependencyScope.CurrentNestedContainer; foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in nestedContainer.GetAllInstances()) { yield return new Filter(filter, FilterScope.Global, order: null); } } } 

用法

请记住,MVC已包含许多filter类型。 甚至基本控制器类型的实现也被注册为全局filter ,因为Controller实现了每种类型的filter。 因此,在注册自定义全局filter类型时需要精确。

选项1:使用基于约定的注册

 // Register the filter provider with MVC. FilterProviders.Providers.Insert(0, new GlobalFilterProvider()); 

然后在你的DI注册

 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(); // Optional: Filter out specific MVC filter types _.Exclude(type => type.Name.EndsWith("Controller")); // Add all filter types. _.AddAllTypesOf(); _.AddAllTypesOf(); _.AddAllTypesOf(); _.AddAllTypesOf(); _.AddAllTypesOf(); // MVC 5 only }); 

注意:您可以通过更改已注册的IFilterProvider实例来控制MVC注册的筛选器。

在此处输入图像描述

所以,替代方案可能是这样的:

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

由于上面的代码没有注册System.Web.Mvc.ControllerInstanceFilterProvider ,因此控制器本身不会被注册为全局filter,因此您不需要将它们过滤掉。 相反,您可以简单地将所有控制器注册为全局filter。

 // Optional: Filter out specific MVC filter types // _.Exclude(type => type.Name.EndsWith("Controller")); 

选项2:个人注册

 // Register the filter provider with MVC. FilterProviders.Providers.Insert(0, new GlobalFilterProvider()); 

然后在你的DI注册

 For().Use(); For().Use();