繁体   English   中英

使用StructureMapFilterProvider进行动作过滤器设置器注入

[英]Action Filter Setter Injection with StructureMapFilterProvider

更新2

再次感谢您提供更详细的信息。 我无法让您的GlobalFilterProvider正常工作,但是看来,无论我在这里采用哪种方法,我都无法利用嵌套容器提供的生命周期范围。 当前,使用StructureMap.MVC5 NuGet包,在Application_BeginRequest上创建了一个嵌套容器,并在Application_EndRequest上对其进行了处理,这使我可以将对象的作用域限定为UniquePerRequest,并在处理嵌套容器时将其拆下。 似乎无论是在Application_Start上添加什么过滤器,还是仍然需要使用容器添加任何依赖项,该容器在Application_Start上仅是当时的父级。 在这种情况下,我什至不认为使用 Decoraptor会对我有帮助。

\n

有什么办法可以做到这一点?

这个问题已经解决-请参阅我的其他SO问题

更新1-NightOwl888的答案评论

我了解您链接到的“对象的设置器”部分中正在发生的事情,因为该代码是StructureMapFilterProvider为建立操作过滤器的依赖关系所做的工作。 我试图理解的是StructureMapFilterProviderIContainer参考。 MVC中是否设置了当前的DependencyResolver ,该IContainer引用的解析正在做什么? 我只想了解它的来源。 另外,添加.Singleton()确实可以解决问题,因此感谢您指出这一点。

我仍在尝试了解您链接到的GlobalFilterProvider ,并且确实了解使用构造函数注入的好处。 我只是想把一切都包好。


原始帖子

我正在使用满足我的DI需求的StructureMap,并已通过StructureMap.MVC5 NuGet包将其包含在ASP.NET MVC 5项目中。 我有一个典型的日志记录操作过滤器,我想在其中创建一个依赖项(ILogger)到创建的操作过滤器中。 虽然我发现并理解了绕过setter注入的被动属性方法,但我也对使用像这样的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;
    }
}

但是, StructureMapFilterProviderIContainer依赖性似乎有些神奇。 我的第一个问题是如何解决? 如果容器本身就是需要解决的问题,那么该怎么办。 第二, StructureMap.MVC5安装程序创建的嵌套容器似乎有问题,并在Application_BeginRequestApplication_EndRequest处处理每个请求。

Application_Start ,筛选器提供程序可以正常工作,大概是因为它还不是嵌套容器,而且解析的IContainer实例如下所示:

在此处输入图片说明

但是,如果在应用程序启动后我再次运行相同的操作,则过滤器提供程序炸弹,并且IContainer实例现在看起来像这样:

在此处输入图片说明

我究竟做错了什么? 有解决办法吗?


如果有帮助,我的其余代码在下面。

记录器的实现:

public class Logger : ILogger
{
    public void Log(string message)
    {
        Debug.WriteLine(message);
    }
}

public interface ILogger
{
    void Log(string message);
}

记录器操作过滤器:

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

由StructureMap.MVC5软件包提供的DependencyResolution / IoC.cs类中添加了其他DI设置:

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

控制器:

public class HomeController : Controller
{
    [Logger]
    public ActionResult Index()
    {
        return Content("worked");
    }
}

二传手注射

没魔术 其他DI软件包会FilterAttribute MVC框架以提供FilterAttribute子类的setter注入 FilterAttribute通常由FilterAttributeFilterProvider加载,但是在这种情况下,它由StructureMapFilterProvider子类StructureMapFilterProvider ,以便提供注入MVC FilterAttributes的扩展点。

看一下StructureMap的Setter注入文档中的“填充对象的Setter” -这基本上是您可以在没有属性的情况下使用Setter注入的方法。 您只需要一个容器注册:

x.For<IGateway>().Use(theGateway);
x.Policies.SetAllProperties(y => y.OfType<IGateway>());

并调用容器:

var target = new BuildUpTarget1();
container.BuildUp(target);

IMO是使用setter注入的最佳方法,因为这意味着您不必引用StructureMap即可使用其.NET属性。

我可能是错的,但是您链接到的帖子中的代码可能已损坏,因为它们没有从MVC的FilterProviders.Providers静态属性中删除默认的FilterAttributeFilterProvider ,并且他们还(通过DI)添加了StructureMapFilterProvider ,我相信它将注册属性两次 (一次与容器相关,一次与null相关)。

构造函数注入

但是,构造函数注入是在应用程序中使用DI的更好,更一致的方法。 IMO,如果可以进行构造函数注入, 永远不要使用其他选项,在这种情况下, 肯定是

可以在Mark Seemann的Passive Attributes帖子中使用一些改进的一件事是可以控制全局注册的过滤器的生存期的功能,因此您可以将非单例注入到过滤器中(请考虑DbContext )。 如果您在GlobalFilters.Filters静态集合中注册过滤器,则所有依赖项都是静态单例的俘虏依赖性 ,这使得它们在定义上成为单例。 事实证明,使用自定义过滤器提供程序解决此问题非常容易。 但是,而不是财产注射建立,我们可以从过滤器类分裂我们的属性类(如果需要),而直接从容器解析过滤器的依赖关系图。

有了它-在组合根中向MVC注册的单个基础结构组件,以及过滤器(及其依赖项)的注册,然后新过滤器在注册后就“正常工作”。 他们甚至不在乎其依赖来自何处。 在注册过程中或使用特定于容器的.NET属性装饰属性类以进行setter注入(这又需要不属于它们的DLL引用)时,不会尴尬的SetAllProperties调用。 无需在FilterAttributes上创建公共属性,这些属性神秘地填充了相关性(也可以不是)。 有了适当的保护子句,就不必担心会注入null setter依赖项。 这是您在应用程序的一部分中使用setter注入的首尾原因 ,只是因为您懒于为使用构造函数注入向MVC添加适当的扩展,并且懒于通过将服务(过滤器)与元数据分离来遵循SRP (.NET属性),并在需要注入依赖项时将FilterAttribute及其所有派生类FilterAttribute窗外。

现在,启动应用程序后,我再次运行相同的操作,筛选器提供程序炸弹

我究竟做错了什么? 有解决办法吗?

由于筛选器提供程序是通过DI注册的,其生存期为“每个请求”(默认),因此将在每个请求上实例化它。 我敢打赌,在构建容器之后,您不能对该容器进行静态访问,因此您会收到错误消息,因为该容器在应用程序启动后超出了范围。

如果将生存期更改为单例(或通过静态FilterProviders.Providers属性进行注册),它将保留其对全局DI容器俘虏的引用,因此不会超出范围。

c.For<IFilterProvider>().Singleton().Use<StructureMapFilterProvider>();

我试图理解的是StructureMapFilterProviderIContainer参考。 MVC中是否设置了当前的DependencyResolver ,该IContainer引用的解析正在做什么?

实际上,如果在调用DependencyResolver.SetResolver之前放置一个断点,然后在立即窗口中运行以下行,则可以看到StructureMap在容器中注册了其自身的实例。

container.GetInstance<IContainer>()

您可以通过尝试解析已解析的容器中注册的一种类型来证明这一点。

container.GetInstance<IContainer>().GetInstance<ILogger>()

因此,正是StructureMap表现出这种行为。 我使用过的大多数其他DI容器都不会像这样进行自我注册(但是可以手动进行注册)。

显然,自注册的IContainer实例未注册为单例。 因此,正如我之前提到的,将IContainer注入到其中的任何基础结构组件都应该是singleton 如果不是,它将丢失对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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM