最终解决方案
在@ NightOwl888的答案的帮助下,这是我最后一次来到这里的人:
1)添加了全局过滤器提供程序:
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);
}
}
}
2)在FilterProviders集合中注册它:
public static void Application_Start()
{
// other bootstrapping code...
FilterProviders.Providers.Insert(0, new GlobalFilterProvider());
}
3)使用passive attributes方法添加了自定义过滤器:
[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<SomeAttribute>().Any())
return;
_dependency.DoWork();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
4)然后在StructureMap中连接所有内容(在此解决方案中,SomeAttribute和GlobalFilterProvider类位于根文件夹中的#34; Filters&#34;文件夹中):
public class ActionFilterRegistry : Registry
{
public ActionFilterRegistry()
{
Scan(s =>
{
// find assembly containing custom filters
s.AssemblyContainingType<GlobalFilterProvider>();
// limit it to the folder containing custom filters
s.IncludeNamespaceContainingType<GlobalFilterProvider>();
// 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<IActionFilter>();
});
}
}
原帖
我目前在ASP.NET MVC 5应用程序中使用StructureMap的每个请求使用嵌套容器。我正在利用structuremap.mvc5
nuget包为我设置所有DI基础设施(依赖解析器,连接容器以及在App_BeginRequest
和{{1}上创建和处理嵌套容器})。我现在需要在动作过滤器中执行一些DI,以便自动执行某些功能。经过大量的研究,我试图这样做,而不需要使用Mark Seemann的passive attributes方法进行二次注射。
在构建属性和过滤器时,一切似乎都很好,直到我在App_Start中注册带有全局过滤器集合的过滤器。我有一个依赖项,我希望每个请求只创建一次,这样不仅动作过滤器,而且在请求期间使用的其他非过滤器基础结构类,可以在整个请求中使用该依赖项的相同实例。如果嵌套容器正在解析依赖项,则默认情况下会这样做。但是,因为我必须在App_Start中注册新过滤器,所以我无法访问嵌套容器。
例如,我的global.asax:
App_EndRequest
有谁知道如何解决这个问题?我也通过这个other SO question遇到了decoraptor解决方案,但是在我的过滤器中使用抽象工厂只会创建一个新的依赖实例,而不是使用我想要的单个请求实例。 / p>
我提出的唯一其他解决方案是将setter注入与自定义过滤器提供程序一起使用,该提供程序使用在全局中创建的静态StructureMapDependencyScope实例,如下所示:
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<ISomeDependency>()));
}
protected void Application_BeginRequest()
{
StructureMapDependencyScope.CreateNestedContainer();
}
protected void Application_EndRequest()
{
HttpContextLifecycle.DisposeAndClearAll();
StructureMapDependencyScope.DisposeNestedContainer();
}
protected void Application_End()
{
StructureMapDependencyScope.Dispose();
}
}
虽然这似乎没有用,但它看起来有点脏。
答案 0 :(得分:2)
您可以构建自定义过滤器提供程序(如this answer中所述)来控制过滤器的生命周期,而不是在静态GlobalFilters.Filters
集合中注册它们。
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);
}
}
}
请记住,MVC已包含许多过滤器类型。甚至基本控制器类型的实现也被注册为全局过滤器,因为Controller
实现了每种类型的过滤器。因此,在注册自定义全局过滤器类型时需要精确。
// 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<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
});
注意:您可以通过更改已注册的
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
,因此控制器本身不会注册为全局过滤器,因此您无需将其过滤掉。相反,您可以简单地将所有控制器注册为全局过滤器。
// 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());
然后在您的DI注册
For<IActionFilter>().Use<MyGlobalActionFilter>();
For<IActionFilter>().Use<MyOtherGlobalActionFilter>();