更新2
<击> 再次感谢您提供更详细的信息。我无法使你的GlobalFilterProvider正常工作,但似乎无论我采用哪种方法,我都无法利用嵌套容器可用的生命周期范围。目前,使用StructureMap.MVC5 NuGet包,在Application_BeginRequest上创建一个嵌套容器,并在Application_EndRequest上处理,这允许我将对象范围限定为UniquePerRequest,并在嵌套容器被放置时将它们拆除。似乎无论在Application_Start上需要添加什么过滤器,仍然需要使用容器添加任何依赖项,而Application_Start上的容器当时只是父项。在这种情况下,我甚至不认为使用Decoraptor会对我有帮助。
有没有办法实现这个目标?
击>
此问题已解决 - 请参阅我的other SO question。
更新1 - 关于NightOwl888答案的评论
我理解您链接到的“填充Setter的一个对象”部分中发生了什么,因为该代码是StructureMapFilterProvider
正在做的用于构建动作过滤器的依赖关系的内容。我想要了解的是IContainer
中的StructureMapFilterProvider
引用。在MVC中设置的当前DependencyResolver
是在解析IContainer
引用吗?我只是想了解它的来源。此外,添加.Singleton()确实解决了问题,所以感谢你指出这一点。
我仍然试图了解您链接的GlobalFilterProvider
,并且我确实理解使用构造函数注入的好处。我只是试着把头包裹起来。
原帖
我正在使用StructureMap来满足我的DI需求,并通过StructureMap.MVC5
NuGet包将它包含在我的ASP.NET MVC 5项目中。我有一个典型的日志记录操作过滤器,我想在我创建的动作过滤器中注入一个依赖项(ILogger)。虽然我已经找到并理解绕过二传手注射的passive attributes方法,但我也对使用StructureMapFilterProvider
这样的方法很感兴趣...
public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
private readonly IContainer _container;
public StructureMapFilterProvider(IContainer container)
{
_container = container;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var filter in filters)
{
_container.BuildUp(filter.Instance);
}
return filters;
}
}
然而,IContainer
的{{1}}依赖关系似乎有点神奇。我的第一个问题是如何解决它?如果容器本身是需要解决的问题,那么解决的是什么。其次,StructureMapFilterProvider
设置在StructureMap.MVC5
和Application_BeginRequest
处为每个请求创建和处理嵌套容器时似乎存在问题。
在Application_EndRequest
上,过滤器提供程序工作正常,可能是因为它还不是嵌套容器,并且已解析的Application_Start
实例如下所示:
然而,如果我在启动应用程序时再次运行相同的操作,则过滤器提供程序炸弹和IContainer实例现在看起来像这样:
我做错了什么?有没有解决这个问题?
如果有帮助,我的其余代码就在下面。
记录器实施:
IContainer
记录器操作过滤器:
public class Logger : ILogger
{
public void Log(string message)
{
Debug.WriteLine(message);
}
}
public interface ILogger
{
void Log(string message);
}
其他DI设置添加到StructureMap.MVC5包提供的DependencyResolution / IoC.cs类中:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class LoggerAttribute : ActionFilterAttribute
{
public ILogger Logger { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Logger.Log("Testing: Executing an action");
}
}
控制器:
public static class IoC
{
public static IContainer Initialize()
{
var container = new Container(c =>
{
c.AddRegistry<DefaultRegistry>();
c.For<IFilterProvider>().Use<StructureMapFilterProvider>();
c.For<ILogger>().Use<Logger>();
c.Policies.SetAllProperties(p =>
{
p.OfType<ILogger>();
});
});
return container;
}
}
答案 0 :(得分:1)
没有魔力。其他DI包攻击MVC框架以提供FilterAttribute
子类的 setter injection 。 FilterAttribute
通常由FilterAttributeFilterProvider加载,但在这种情况下,它由StructureMapFilterProvider
子类化,以便提供一个扩展点来注入MVC FilterAttributes
。
查看&#34;填充对象的设置者&#34;在StructureMap Setter Injection Documentation中 - 这基本上就是你可以在没有属性的情况下使用setter注入的方式。您只需要一个容器注册:
x.For<IGateway>().Use(theGateway);
x.Policies.SetAllProperties(y => y.OfType<IGateway>());
并调用容器:
var target = new BuildUpTarget1();
container.BuildUp(target);
IMO是使用setter注入的最佳方式,因为这意味着您不必引用StructureMap以使用其.NET属性。
我可能错了,但the post you linked to中的代码可能已被破坏,因为他们没有从MVC的
FilterAttributeFilterProvider
静态属性中删除默认FilterProviders.Providers
并且他们正在添加StructureMapFilterProvider
以及(通过DI),我相信它会将属性注册两次(一次使用容器依赖项,一次使用null
依赖项)。
但是,构造函数注入是在应用程序中使用DI的更好,更一致的方法。 IMO,如果构造函数注入是可能的,永远不会使用其他选项,在这种情况下,肯定是。
可以在Mark Seemann的Passive Attributes post中使用一些改进的一件事是控制全局注册过滤器的生命周期的能力,因此您可以将非单体注入过滤器(想想DbContext
)。如果在GlobalFilters.Filters
静态集合中注册过滤器,则所有依赖项都是静态单例的captive dependencies,这使它们按照定义单例化。事实证明使用custom filter provider很容易解决这个问题。但是,我们可以从我们的过滤器类中拆分属性类(如果需要),而只是直接从容器中解析过滤器的依赖关系图,而不是使用属性注入构建。
你有它 - 在组合根中注册MVC的单个基础架构组件以及过滤器(及其依赖项)的注册,然后是新的过滤器&#34;只是工作&#34;一旦他们注册。他们甚至不关心他们的依赖来自哪里。注册期间没有笨拙的SetAllProperties
调用或使用特定于容器的.NET属性装饰属性类以进行setter注入(这反过来又要求DLL引用它们不属于的地方)。无需在FilterAttributes
上创建公共属性,这些属性神秘地填充了依赖项(或不是)。使用适当的保护子句,无需担心null
setter注入的依赖项。这是在你的应用程序的 part 中使用setter注入的首要问题,因为你太懒了,无法为MVC添加适当的扩展以便使用构造函数注入,而且懒得通过分离服务来跟踪SRP (过滤)来自元数据(.NET属性)并在需要注入依赖项时将FilterAttribute
及其所有衍生物抛出窗口。
我在应用程序启动时再次运行相同的操作,过滤器提供程序炸弹
我做错了什么?有没有解决这个问题?
由于过滤器提供程序是通过DI注册的,其生存期为Per Request(默认值),因此每次请求都会对其进行实例化。我打赌你在构建容器后没有静态访问容器,所以你得到错误,因为容器在应用程序启动后超出了范围。
如果您将生命周期更改为单例(或通过静态FilterProviders.Providers
属性注册它),它将保留对全局DI容器俘虏的引用,因此它不会超出范围。
c.For<IFilterProvider>().Singleton().Use<StructureMapFilterProvider>();
我想要了解的是
IContainer
中的StructureMapFilterProvider
引用。在MVC中设置的当前DependencyResolver
是在解析IContainer
引用吗?
实际上,如果在调用DependencyResolver.SetResolver
之前放置断点,然后在即时窗口中运行以下行,则可以看到StructureMap在中注册了一个本身的实例。容器。
container.GetInstance<IContainer>()
您可以通过尝试解析已解析容器中注册的其中一种类型来证明它。
container.GetInstance<IContainer>().GetInstance<ILogger>()
因此,StructureMap正在展示这种行为。我使用过的大多数其他DI容器都没有像这样自行注册(但可以手动完成)。
显然,自我注册的IContainer
实例未注册为单例。因此,正如我之前提到的,您注入IContainer
的任何基础架构组件应该是单例。如果不是,它将失去对IContainer
的引用。我不建议你尝试使用IContainer
作为单身人士,因为必须有一个很好的理由说明为什么它不能自我注册。
我仍然试图了解您链接到
的GlobalFilterProvider
实际上,这是完全相同的概念。但在这种情况下,我决定向DI容器注册IDependencyResolver
,因此可以用它来为基础架构组件提供服务。
// Register other container components...
// Create Dependency Resolver
var dependencyResolver = new StructureMapDependencyResolver(container);
// Register the dependency resolver with StructureMap
For<IDependencyResolver>().Use(dependencyResolver);
// Set the dependency resolver for MVC
DependencyResolver.SetResolver(dependencyResolver);
在此示例中注入IDependencyResolver
的原因是什么。
好的,从表面上看,这看起来是不必要的,因为只需将IContainer
注入基础架构组件,就可以自动获取对容器的引用。但是使用IDependencyResolver
(MVC的一部分)可以更容易切换到另一个DI容器,如果你以后决定你选择的那个并不像新的闪亮DI容器那么好你刚刚在一些博客上看过。更不用说,对于那些可能不知道如何实现特定于他们的 DI容器的人来说,对StackOverflow上的人更有用 - 一个通用的更适合用于信息目的。
在我看来,你依赖 DI容器越少,如果你需要更换容器就行更安全(这是另一个反对使用setter注入的论据,因为每个DI容器做的有点不同)。此外,使用IDependencyResolver
(它是MVC的一部分)清楚地将组件描述为 MVC基础架构组件,它仅用作应用程序和MVC之间的代理。这个组件应该被认为是组合根和IMO的一部分,这意味着它的实现应该是MVC项目的一部分,而不是在某个地方养成DLL。但在这种情况下,它也可以被视为MVC扩展代码的一部分,这意味着您可以将它放在放置其他MVC扩展的位置而不必过多担心组合根部分。当您更改组合根时,它不需要更改。
尽管如此,这种方法还有一个缺点。它强制 MVC应用程序使用IDependencyResolver
而不是IControllerFactory
作为DI集成点。如果您将IContainer
注入组件,则可以自由选择使用IControllerFactory
。