Внедрение зависимостей в атрибуты

Я пытаюсь внедрить зависимость в пользовательский AuthorizeAttribute следующим образом:

public class UserCanAccessArea : AuthorizeAttribute
{
    readonly IPermissionService permissionService;

    public UserCanAccessArea() :
        this(DependencyResolver.Current.GetService<IPermissionService>()) { }

    public UserCanAccessArea(IPermissionService permissionService)
    {
        this.permissionService = permissionService;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

Это работает, но, похоже, решается как единое целое, означающее, что я получил проблемы, описанные в моем предыдущем вопрос

Я хотел бы использовать инъекцию свойств, но поскольку сам мой атрибут не разрешен Unity, я не могу найти способ настроить контейнер для перехвата и разрешения свойства. Я пробовал следующее:

public class UserCanAccessArea : AuthorizeAttribute
{
    public IPermissionService permissionService { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

Контейнер:

container.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));

Но свойство всегда нулевое во время выполнения.

Кто-нибудь достиг этого, и если да, то есть ли у вас пример?

Ответ 1

Вам следует избегать полностью вливания зависимостей в атрибуты. Причина этого объясняется в этой статье: Инъекция зависимостей в атрибутах: не делайте этого!. Таким образом, статья объясняет, что:

  • Встраивание конструктора невозможно, так как создание экземпляра атрибута невозможно перехватить; CLR находится под контролем.
  • Использование инъекции свойств является хрупким, так как оно приводит к Temporal Coupling, что должно быть предотвращено.
  • Включение зависимостей в атрибуты делает невозможным проверку правильность конфигурации контейнера.
  • Структуры, такие как атрибуты кеша MVC и веб-API, что позволяет легко создать неактивные зависимости, вызывая ошибки.

Здесь у вас есть два варианта:

  • Сделайте атрибуты пассивными, разделив данные (атрибут) из своего поведения (службы), как описано в ссылка на статью и эта связанная статья от Марка Seemann.
  • Превратите свои атрибуты в скромные объекты, как описано в этом ответе. Это значит, что ты:
    • извлечь всю логику из атрибута в пользовательскую службу, содержащую все зависимости.
    • Зарегистрируйте эту службу в своем контейнере.
    • пусть метод атрибута (AuthorizeCore в вашем случае) не делает ничего, кроме решения службы из локатора службы /DependencyResolver и вызывает метод службы. Важно отметить, что вы не можете выполнять инъекцию конструктора, вложение свойств и не может храниться в приватном состоянии атрибутов (как вы уже заметили).

Какой вариант использовать:

  • Используйте параметр 1, если вы очень заинтересованы в том, чтобы ваш дизайн был чистым, или у вас есть несколько атрибутов, которые вам нужно применять таким образом, или вы хотите применять атрибуты, определены в сборке, которая не зависит от System.Web.Mvc.
  • В противном случае используйте опцию 2.