被动属性和嵌套容器 [英] Passive Attributes and Nested Containers

查看:94
本文介绍了被动属性和嵌套容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最终解决方案

在@ NightOwl888答案的帮助下,这是我为最终在这里遇到的人所采用的最终方法:

With help from @NightOwl888's answer, here's the final approach I went with for anyone who ends up here:

1)添加了全局过滤器提供程序:

1) Added the global filter provider:

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集合中:

2) Registered it in the FilterProviders collection:

public static void Application_Start()
{
    // other bootstrapping code...

    FilterProviders.Providers.Insert(0, new GlobalFilterProvider());
}

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

3) Added a custom filter using the passive attributes approach:

[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类位于根文件夹中的同一"Filters"文件夹中):

4) Then wired everything up in StructureMap (in this solution the SomeAttribute and GlobalFilterProvider classes are in the same "Filters" folder within the root folder):

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_BeginRequestApp_EndRequest上创建和处理嵌套容器).现在,我需要在动作过滤器中执行一些DI以便自动化某些功能.经过大量研究后,我尝试使用Mark Seemann的被动属性方法.

I'm currently using a nested container per request with StructureMap in an ASP.NET MVC 5 application. I'm utilizing the structuremap.mvc5 nuget package to setup all the DI infrastructure for me (the dependency resolver, wiring up the container and creating and disposing of the nested container on App_BeginRequest and App_EndRequest). I'm at the point now where I need to do some DI within action filters in order to automate some functionality. After a good amount of research, I am attempting to do so without the need for setter injection, using Mark Seemann's passive attributes approach.

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

All seemed well and good while building the attribute and filter, until I got to registering the filter with the global filter collection within App_Start. I have a dependency that I would like to be created only once per request so that not only the action filter, but also other non-filter infrastructure classes utilized during a request, can use the same instance of that dependency over the entire request. If the nested container were resolving the dependency, it would do that by default. However, because I have to register the new filter in App_Start, I don't have access to the nested container.

例如,我的global.asax:

For example, my 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<ISomeDependency>()));
    }

    protected void Application_BeginRequest()
    {
        StructureMapDependencyScope.CreateNestedContainer();
    }

    protected void Application_EndRequest()
    {
        HttpContextLifecycle.DisposeAndClearAll();
        StructureMapDependencyScope.DisposeNestedContainer();
    }

    protected void Application_End()
    {
        StructureMapDependencyScope.Dispose();
    }
}

有人知道如何解决这个问题吗?我也通过

Does anyone know how to solve this? I've come across the decoraptor solution as well via this other SO question, but using an abstract factory within my filter would just create a new instance of the dependency, rather than using the single per request instance I want.

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

The only other solution I have come up with was to use setter injection with a custom filter provider that uses the static StructureMapDependencyScope instance created in the global, like this:

public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
    public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        var filters = base.GetFilters(controllerContext, actionDescriptor);

        foreach (var filter in filters)
        {
            MvcApplication.StructureMapDependencyScope.CurrentNestedContainer.BuildUp(filter.Instance);
        }

        return filters;
    }
}

虽然看起来可以正常工作,但看起来有点脏.

While that seems to work alright, it just seems a little dirty.

推荐答案

您可以构建自定义过滤器提供程序(如

You can build a custom filter provider (as in this answer) to control the lifetime of the filters, rather than registering them in the static GlobalFilters.Filters collection.

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类型的实现都被注册为全局过滤器,因为Controller实现了每种类型的过滤器.因此,在注册自定义全局过滤器类型时,需要保持精确.

Usage

Keep in mind, MVC already contains lots of filter types. Even implementations of the base Controller type are registered as global filters, as Controller implements every type of filter. So, you need to be precise when you register your custom global filter types.

// 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寄存器.

NOTE: You can control which filters MVC registers by changing the IFilterProvider instances that are registered.

因此,另一种选择可能是:

So, an alternative could be something like:

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,因此控制器本身不会注册为全局过滤器,因此您无需将其过滤掉.相反,您可以简单地让所有控制器注册为全局过滤器.

Since the above code does not register the System.Web.Mvc.ControllerInstanceFilterProvider, the Controllers themselves won't be registered as global filters, so you wouldn't need to filter them out. Instead, you could simply let all of your controllers be registered as global filters.

// 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<IActionFilter>().Use<MyGlobalActionFilter>();
For<IActionFilter>().Use<MyOtherGlobalActionFilter>();

这篇关于被动属性和嵌套容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆