如何解决MVC Filter属性中的依赖注入问题

时间:2022-11-04 15:49:48

I have a custom attribute class derived from AuthorizationAttribute, which performs custom security on controller actions. The OnAuthorizationCore method depends on various other components (e.g. DAL) in order to ajudicate whether a user can invoke an action.

我有一个派生自AuthorizationAttribute的自定义属性类,它对控制器操作执行自定义安全性。 OnAuthorizationCore方法依赖于各种其他组件(例如DAL)以判断用户是否可以调用操作。

I'm using Autofac for dependency injection. The ExtensibleActionInvoker claims to be able to perform property injection on action filters. Setting an attribute's properties at runtime (which seems like a bad idea) will work in a simple unit test, but in a busy, multi-threaded web server it's bound to go wrong, and so this idea seems like an anti-pattern. Hence this question:

我正在使用Autofac进行依赖注入。 ExtensibleActionInvoker声称能够在动作过滤器上执行属性注入。在运行时设置属性的属性(这似乎是一个坏主意)将在一个简单的单元测试中工作,但在繁忙的多线程Web服务器中它肯定会出错,所以这个想法看起来像一个反模式。因此这个问题:

If my AuthorizationAttribute depends on other components in order to work correctly, what it the right [architecture] pattern in order to achieve this?

如果我的AuthorizationAttribute依赖于其他组件才能正常工作,为了实现这一点,它的正确[架构]模式是什么?

i.e. AuthorizationAttribute depends on IUserRepository... how should this relationship be resolved?

即AuthorizationAttribute依赖于IUserRepository ......如何解决这种关系?

4 个解决方案

#1


17  

The ExtensibleActionInvoker claims to be able to perform property injection on action filters.

ExtensibleActionInvoker声称能够在动作过滤器上执行属性注入。

Correct - but don't confuse action filters with the attributes that might not implement them. The cleanest way to approach this in ASP.NET MVC is to split responsibilities, even though the MVC framework allows you to combine them.

正确 - 但不要将动作过滤器与可能无法实现它们的属性混淆。在ASP.NET MVC中解决此问题的最简单方法是分割职责,即使MVC框架允许您将它们组合在一起。

E.g., use a pair of classes - an attribute class that holds data only:

例如,使用一对类 - 仅包含数据的属性类:

// Just a regular old attribute with data values
class SomeAttribute : Attribute { ... }

And a filter that has dependencies injected:

并且注入了依赖项的过滤器:

// Gets dependencies injected
class SomeFilter : IActionFilter { ... }

SomeFilter just uses the typical approach of getting the SomeAttribute attribute from the controller or action method via GetCustomAttributes() to do whatever work is needed.

SomeFilter只使用通过GetCustomAttributes()从控制器或操作方法获取SomeAttribute属性的典型方法来完成所需的任何工作。

You can then use ExtensibleActionInvoker to wire up the filter:

然后,您可以使用ExtensibleActionInvoker连接过滤器:

builder.RegisterControllers(...).InjectActionInvoker();
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterType<SomeFilter>().As<IActionFilter>();

It might be a little more code than you'd write using the attribute-as-filter approach, but the quality of the code will be better in the long run (e.g. by avoiding the limitations of attributes and the awkwardness of the Service Locator solutions.)

它可能比使用属性为过滤器的方法编写的代码多一点,但从长远来看代码的质量会更好(例如,通过避免属性的限制和服务定位器解决方案的尴尬。)

#2


2  

I would seem that the easiest way to achieve this is to bite the bullet and accept a dependency on autofac itself. While a dependency on the IoC is in itself an anti-pattern, it's somewhat more pallatable. You can implement a property as follows:

我认为最简单的方法是咬紧牙关并接受对autofac本身的依赖。虽然对IoC的依赖本身就是一种反模式,但它更适合。您可以按如下方式实现属性:

public class UserAuthorizeAttribute : AuthorizeAttribute
{            
    public IUserRepository CurrentUserService
    {
        get
        {
            var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
            var cp = cpa.ContainerProvider;
            return cp.RequestLifetime.Resolve<IUserRepository>();
        }
    }
}
     ...

#3


2  

There's no straight-forward way to do this prior to MVC2. There is an interesting technique detailed here: http://www.mattlong.com.au/?p=154. I'd suggest using the Common Service Locator to abstract over this and locate your DI container.

在MVC2之前没有直接的方法来做到这一点。这里有一个有趣的技术:http://www.mattlong.com.au/?p = 154。我建议使用Common Service Locator对此进行抽象并找到您的DI容器。

If you're using MVC 3 then you can use MVC Service Location

如果您正在使用MVC 3,那么您可以使用MVC服务位置

#4


0  

Constructor injection seems to be impossible w/o changing way of filter registration.

构造函数注入似乎不可能不改变过滤器注册的方式。

Even in Asp.Net Mvc3:

即使在Asp.Net Mvc3中:

One place where dependency injection has been difficult in the past is inside the filter attributes themselves. Because the .NET framework runtime is actually responsible for creating these attribute instances, we cannot use a traditional dependency injection strategy.

过去依赖注入困难的一个地方是过滤器属性本身。由于.NET框架运行时实际上负责创建这些属性实例,因此我们无法使用传统的依赖注入策略。

So - next best thing is property injection (Mvc3 provides some support for that out of the box).

所以 - 下一个最好的东西是属性注入(Mvc3为开箱即用提供了一些支持)。

Here's a how to for doing that manually.

以下是如何手动执行此操作。

I personally use MvcExtensions. I'm fine with registering them in different way. Here's usage.

我个人使用MvcExtensions。我可以用不同的方式注册它们。这是用法。

Another thing You might want to investigate is MvcTurbine project. In contrast to MvcExtensions project which is more general - MvcTurbine is primarily for providing dependency injection support.

您可能想要调查的另一件事是MvcTurbine项目。与更为通用的MvcExtensions项目相比 - MvcTurbine主要用于提供依赖注入支持。

#1


17  

The ExtensibleActionInvoker claims to be able to perform property injection on action filters.

ExtensibleActionInvoker声称能够在动作过滤器上执行属性注入。

Correct - but don't confuse action filters with the attributes that might not implement them. The cleanest way to approach this in ASP.NET MVC is to split responsibilities, even though the MVC framework allows you to combine them.

正确 - 但不要将动作过滤器与可能无法实现它们的属性混淆。在ASP.NET MVC中解决此问题的最简单方法是分割职责,即使MVC框架允许您将它们组合在一起。

E.g., use a pair of classes - an attribute class that holds data only:

例如,使用一对类 - 仅包含数据的属性类:

// Just a regular old attribute with data values
class SomeAttribute : Attribute { ... }

And a filter that has dependencies injected:

并且注入了依赖项的过滤器:

// Gets dependencies injected
class SomeFilter : IActionFilter { ... }

SomeFilter just uses the typical approach of getting the SomeAttribute attribute from the controller or action method via GetCustomAttributes() to do whatever work is needed.

SomeFilter只使用通过GetCustomAttributes()从控制器或操作方法获取SomeAttribute属性的典型方法来完成所需的任何工作。

You can then use ExtensibleActionInvoker to wire up the filter:

然后,您可以使用ExtensibleActionInvoker连接过滤器:

builder.RegisterControllers(...).InjectActionInvoker();
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterType<SomeFilter>().As<IActionFilter>();

It might be a little more code than you'd write using the attribute-as-filter approach, but the quality of the code will be better in the long run (e.g. by avoiding the limitations of attributes and the awkwardness of the Service Locator solutions.)

它可能比使用属性为过滤器的方法编写的代码多一点,但从长远来看代码的质量会更好(例如,通过避免属性的限制和服务定位器解决方案的尴尬。)

#2


2  

I would seem that the easiest way to achieve this is to bite the bullet and accept a dependency on autofac itself. While a dependency on the IoC is in itself an anti-pattern, it's somewhat more pallatable. You can implement a property as follows:

我认为最简单的方法是咬紧牙关并接受对autofac本身的依赖。虽然对IoC的依赖本身就是一种反模式,但它更适合。您可以按如下方式实现属性:

public class UserAuthorizeAttribute : AuthorizeAttribute
{            
    public IUserRepository CurrentUserService
    {
        get
        {
            var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
            var cp = cpa.ContainerProvider;
            return cp.RequestLifetime.Resolve<IUserRepository>();
        }
    }
}
     ...

#3


2  

There's no straight-forward way to do this prior to MVC2. There is an interesting technique detailed here: http://www.mattlong.com.au/?p=154. I'd suggest using the Common Service Locator to abstract over this and locate your DI container.

在MVC2之前没有直接的方法来做到这一点。这里有一个有趣的技术:http://www.mattlong.com.au/?p = 154。我建议使用Common Service Locator对此进行抽象并找到您的DI容器。

If you're using MVC 3 then you can use MVC Service Location

如果您正在使用MVC 3,那么您可以使用MVC服务位置

#4


0  

Constructor injection seems to be impossible w/o changing way of filter registration.

构造函数注入似乎不可能不改变过滤器注册的方式。

Even in Asp.Net Mvc3:

即使在Asp.Net Mvc3中:

One place where dependency injection has been difficult in the past is inside the filter attributes themselves. Because the .NET framework runtime is actually responsible for creating these attribute instances, we cannot use a traditional dependency injection strategy.

过去依赖注入困难的一个地方是过滤器属性本身。由于.NET框架运行时实际上负责创建这些属性实例,因此我们无法使用传统的依赖注入策略。

So - next best thing is property injection (Mvc3 provides some support for that out of the box).

所以 - 下一个最好的东西是属性注入(Mvc3为开箱即用提供了一些支持)。

Here's a how to for doing that manually.

以下是如何手动执行此操作。

I personally use MvcExtensions. I'm fine with registering them in different way. Here's usage.

我个人使用MvcExtensions。我可以用不同的方式注册它们。这是用法。

Another thing You might want to investigate is MvcTurbine project. In contrast to MvcExtensions project which is more general - MvcTurbine is primarily for providing dependency injection support.

您可能想要调查的另一件事是MvcTurbine项目。与更为通用的MvcExtensions项目相比 - MvcTurbine主要用于提供依赖注入支持。