被动属性和嵌套容器
最终解决方案
在@ 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_BeginRequest
和App_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();