0

我有一个全局过滤器属性,需要访问每个 HTTP 请求注册的项目:

// other ContainerBuilder stuff
builder.RegisterType<HttpDependency>().As<IHttpDependency>().InstancePerHttpRequest();

在其他地方:

internal sealed class MyActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // EVIL YUCKY SERVICE LOCATOR!
        var resolved = AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<IHttpDependency>();

        if (resolved.NeedsRedirect)
        {
            // does a redirect
        }

        base.OnActionExecuting(filterContext);
    }
}

并将其注册为全局过滤器:

// in FilterConfig.cs
filters.Add(new MyActionFilter());

由于这是一个全局过滤器,我不能使用构造函数注入,即应用程序启动时的 HTTP 上下文不应该被每个请求重用。如何在不通过服务定位器伸出手并抓住它的情况下正确连接它?

4

2 回答 2

1

一种方法是从属性中删除逻辑并在实现 IActionFilter 的类中实现它。然后将该类注册到容器中,以便依赖注入能够正常工作。Orchard CMS 使用这种方法。

public class MyCustomActionFilterAttribute : Attribute
{
}

public class MyCustomActionFilter : FilterProvider, IActionFilter
{
    protected MyService Service { get; private set; }

    // MyService can be injected by the container...
    public MyCustomActionFilter(MyService service)
    {
        this.Service = service;
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Check to see if the action has a matching attribute
        var attributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MyCustomActionFilterAttribute), true);

        // Perform some logic here....
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
    }
}

可以创建一个将过滤器应用于操作的 IActionInvoker,此类使用 DependencyResolver 自动实例化我的 MVC。

public class FilterResolvingActionInvoker : ControllerActionInvoker
{
    protected IEnumerable<IFilterProvider> Providers { get; private set; }

    // Filters registered with the container are injected by the container
    public FilterResolvingActionInvoker(IEnumerable<IFilterProvider> providers)
    {
        this.Providers = providers;
    }

    // Add the filter to the current FilterInfo
    protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        var filters = base.GetFilters(controllerContext, actionDescriptor);
        foreach (var provider in this.Providers)
        {
            provider.AddFilters(filters);
        }
        return filters;
    }
}

定义一个允许我们注册过滤器的通用接口。

public interface IFilterProvider
{
    void AddFilters(FilterInfo filterInfo);
}

public abstract class FilterProvider : IFilterProvider
{
    public void AddFilters(FilterInfo filterInfo)
    {
        if (this is IActionFilter)
        {
            filterInfo.ActionFilters.Add(this as IActionFilter);
        }
    }
}

并将它们注册到容器构建器。也可以为 Autofac 创建一个扩展方法,以在您的程序集中自动注册所有 IFilterProviders。

builder.RegisterType<FilterResolvingActionInvoker>().As<IActionInvoker>().InstancePerDependency();
builder.RegisterType<MyCustomActionFilter>().As<IFilterProvider>().InstancePerDependency();
于 2013-07-02T21:45:26.853 回答
-1

与往常一样,避免定位器的选项之一是在 Compositon Root 中设置本地工厂。工厂设置为使用您的 ioc 容器。

http://netpl.blogspot.com/2012/12/di-factories-and-composition-root.html

尽管您可能会争辩说“技术上”这个“看起来像”定位器(您创建一个工厂实例并要求它提供服务),但它不会向任何其他基础设施引入任何依赖关系,包括您最终用于实现的实际 IoC 容器factory - 实际工厂的实现是 Composition Root 的一部分(接近全局应用程序类)。

这种方法导致许多孤立的小工厂负责您的基础设施的一部分,但每个工厂仍然有一个可插入的提供程序,您可以在组合根附近实现,从而避免任何外部依赖。

于 2013-07-02T21:11:58.910 回答