2012-01-06 3 views
6

У меня возникла интересная проблема дизайна с библиотекой классов, которую я пишу. У меня есть пользовательские реализации AuthorizeAttribute, что я хочу, чтобы клиенты могли использовать так:Включение зависимостей в AuthorizeAttribute в библиотеке классов

[Protected("permission_name")] 

В приведенной выше коде, PermissionAttribute наследует от AuthorizeAttribute и использует локальное по умолчанию (DefaultContext создана при HttpContext).

За кулисами атрибут использует SecurityService для проверки пользователей, ролей и разрешений (сам SecurityService использует предоставленную клиентом службу сохранения, которую они могут подключить в корневом каталоге их приложения).

Таким образом, моим атрибутам нужна ссылка на функцию SecurityService. Поскольку конструкторы атрибутов могут иметь только константы времени компиляции, я не могу использовать инъекцию конструктора.

Я не хочу, чтобы заставить своих клиентов использовать рамки DI - они должны быть в состоянии обнаружить и проволоки до необходимых зависимостей в их составе корня без использования библиотеки IoC, если они пожелают.

Вот мои варианты:

  • есть библиотека использовать синглтон SecurityService.
  • Встраиваться свойство, которое будет работать, но
    1. это сделает зависимость, кажется, необязательно, что это не так и
    2. Я не знаю, где я могу сделать инъекции собственности в приложение MVC на атрибуте авторизовать ,

Возможное решение 2 выше делать множество экземпляра SecurityService как статическое свойство на атрибуте при запуске приложения и использовать пункт охраны, чтобы предотвратить его установить более чем один раз, как это:

class ProtectedAttribute : ... 
{ 
    private static ISecurityService _SecurityService ; 
    public static ISecurityService SecurityService 
    { 
     get 
     { 
      return _SecurityService ; 
     } 
     set 
     { 
      if (_SecurityService != null) 
       throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ; 
      _SecurityService = value ; 
     } 
    } 
} 

SecurityService может быть абстрактным фасад службы, так что она может быть расширена/заменена другой реализации.

Есть ли лучший способ решить эту проблему?

UPDATE: Добавление некоторого кода, чтобы показать, как я собираюсь сделать это:

Добавить общественную собственность на атрибут, который возвращает имя разрешения:

public class ProtectedAttribute : ... 
{ 
    private string _Permission ; 
    public string Permission { get { return _Permission ; } /*...*/ } 

    public ProtectedAttribute(string permission) { /*...*/ } 
} 

Настройка фильтра авторизации и настроить зависимость через Ninject (при использовании Ninject):

using Ninject.Web.Mvc.FilterBindingSyntax; 

public class MyModule : Ninject.Modules.NinjectModule 
{ 
    public override void Load() 
    { 
    // mySecurityService instance below can have a singleton lifetime - perfect! 
    this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0) 
     .WhenActionMethodHas<ProtectedAttribute>() 
     .WithConstructorArgument("securityService", mySecurityService) 
     .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ; 
    } 
} 

Оооо это ... красивый сопеть

+0

Является ли этот ASP.NET MVC 3? –

+0

@ Darin: Да, требуется минимум MVC 3. Обновлены теги. –

+1

Связанные: http://stackoverflow.com/questions/7192543/injecting-dependencies-into-asp-net-mvc-3-action-filters-whats-wrong-with-this/7194467#7194467 –

ответ

7

С помощью ASP.NET MVC 3 вы можете использовать инъекцию конструктора с фильтрами действий благодаря новому IFilterProvider. Таким образом вам больше не нужно украшать действия вашего контроллера фильтрами действий.Вы можете применить их благодаря этому интерфейсу и использовать атрибут маркера.

И если вы не хотите выполнять его вручную, вы всегда можете использовать существующую инфраструктуру DI, такую ​​как Ninject, которая предоставляет fluent way для определения зависимостей фильтра действий.

+0

+1 для 'IFilterProvider 'который дает довольно чистое решение. Аналогичная проблема [ASP.NET MVC IFilterProvider и разделение проблем] (http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider-and-separation-of-concerns) (замок Виндзор). –

0

Мои приложения наследуются от базового класса Application, который предоставляет контейнер IOC.

public interface IInjectableApplication 
    { 
     IUnityContainer Container { get; } 
    } 

Тогда у меня есть базовый класс атрибут, который знает об этом

public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{ 
    protected T ResolveItem<T>(ResultExecutedContext context) 
     { 
      var app = context.HttpContext.ApplicationInstance as IInjectableApplication; 
      if (app == null) { throw new NullReferenceException("Application is not IInjectable."); } 

      T c = (T)app.Container.Resolve(typeof(T)); 

      if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); } 
      return c; 
     } 

} 

Хотя это не верно для инъекций, так как атрибуты не строятся «нормально», это обеспечивает подобное поведение. Нет причин, по которым он не должен быть адаптирован к другим МОК

Смежные вопросы