У меня возникла интересная проблема дизайна с библиотекой классов, которую я пишу.У меня есть пользовательская реализация AuthorizeAttribute, которую я хочу, чтобы клиенты могли использовать следующим образом:
[Protected("permission_name")]
В приведенном выше коде PermissionAttribute наследуется от AuthorizeAttribute и использует локальное значение по умолчанию (DefaultContext, созданный с использованием HttpContext).
За кулисами атрибут использует SecurityService для проверки пользователей, ролей и разрешений (сам SecurityService использует предоставляемую клиентом службу персистентности, которую они могут подключить в корне композиции своего приложения).
Так что для работы моих атрибутов нужна ссылка на SecurityService.Поскольку конструкторы атрибутов могут иметь только константы времени компиляции, я не могу использовать внедрение конструктора.
Я не хочу заставлять своих клиентов использовать инфраструктуру DI - они должны иметь возможность обнаружить и подключите необходимые зависимости в их корне композиции без использования библиотеки IoC, если они того пожелают.
Вот мои варианты:
- Есть библиотекаиспользовать одноэлементную SecurityService.
- Использовать внедрение свойства, которое бы работало, но
- это сделало бы зависимость необязательной, чего нет, и
- Я не знаю, гдеЯ могу выполнить внедрение свойства в приложении MVC для атрибута authorize.
Возможное решение для 2. выше состоит в том, чтобы установить экземпляр SecurityService в качестве статического свойства наАтрибут при запуске приложения и используйте пункт guard для предотвращения его установки более одного раза, например:
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 может быть абстрактным сервисным фасадомe, чтобы его можно было расширить / заменить другой реализацией.
Есть ли лучший способ решить эту проблему?
ОБНОВЛЕНИЕ: добавление некоторого кода, чтобы показать, как я собираюсь это сделать:
Добавить открытое свойство в атрибуте, который возвращает имя разрешения:
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) ;
}
}
Ооо, это ... прекрасно понюхать