Единство внедрения зависимостей - условное разрешение
Условное разрешение - последнее, чего я не понимаю в данный момент.
Допустим, у нас есть интерфейс IAuthenticate
:
public interface IAuthenticate{
bool Login(string user, string pass);
}
Теперь у меня есть два типа аутентификации.
Твиттер авторизация
public class TwitterAuth : IAuthenticate
{
bool Login(string user, string pass)
{
//connect to twitter api
}
}
Facebook Auth
public class FacebookAuth: IAuthenticate
{
bool Login(string user, string pass)
{
//connect to fb api
}
}
Регистрация типов в едином конфиге:
unityContainer.RegisterType<IAuthenticate, TwitterAuth>();
unityContainer.RegisterType<IAuthenticate, FacebookAuth>();
вводить объекты через DI в нашем контроллере:
private readonly IAuthenticate _authenticate;
public AuthenticateController(IAuthenticate authenticate)
{
_authenticate = authenticate;
}
// login with twitter
public virtual ActionResult Twitter(string user, string pass)
{
bool success =
_authenticate.Login(user, pass);
}
// login with fb
public virtual ActionResult Facebook(string user, string pass)
{
bool success =
_authenticate.Login(user, pass);
}
// login with google
public virtual ActionResult Google(string user, string pass)
{
bool success =
_authenticate.Login(user, pass);
}
Как именно Unity узнает, какой объект нужно разрешить для разных типов аутентификации? Как мне сделать условное разрешение в этом случае?
Я говорил с моим другом, и он объяснил, что если эта ситуация кажется неправильной, то это просто фабричный образец.
2 ответа
Простой способ решить это - с помощью шаблона стратегии. Обратите внимание, что вы можете добавлять или удалять провайдеров входа в систему без изменения дизайна - вам просто нужно изменить конфигурацию DI.
Интерфейсы
public interface IAuthenticate{
bool Login(string user, string pass);
bool AppliesTo(string providerName);
}
public interface IAuthenticateStrategy
{
bool Login(string providerName, string user, string pass);
}
Провайдеры аутентификации
public class TwitterAuth : IAuthenticate
{
bool Login(string user, string pass)
{
//connect to twitter api
}
bool AppliesTo(string providerName)
{
// I used the type name for this example, but
// note that you could use any string or other
// datatype to select the correct provider.
return this.GetType().Name.Equals(providerName);
}
}
public class FacebookAuth: IAuthenticate
{
bool Login(string user, string pass)
{
//connect to fb api
}
bool AppliesTo(string providerName)
{
return this.GetType().Name.Equals(providerName);
}
}
стратегия
public class AuthenticateStrategy: IAuthenticateStrategy
{
private readonly IAuthenticate[] authenticateProviders;
public AuthenticateStrategy(IAuthenticate[] authenticateProviders)
{
if (authenticateProviders == null)
throw new ArgumentNullException("authenticateProviders");
this.authenticateProviders = authenticateProviders;
}
public bool Login(string providerName, string user, string pass)
{
var provider = this.authenticateProviders
.FirstOrDefault(x => x.AppliesTo(providerName));
if (provider == null)
{
throw new Exception("Login provider not registered");
}
return provider.Login(user, pass);
}
}
Единство регистрации
// Note that the strings used here for instance names have nothing
// to do with the strings used to select the instance in the strategy pattern
unityContainer.RegisterType<IAuthenticate, TwitterAuth>("twitterAuth");
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("facebookAuth");
unityContainer.RegisterType<IAuthenticateStrategy, AuthenticateStrategy>(
new InjectionConstructor(
new ResolvedArrayParameter<IAuthenticate>(
new ResolvedParameter<IAuthenticate>("twitterAuth")
),
new ResolvedArrayParameter<IAuthenticate>(
new ResolvedParameter<IAuthenticate>("facebookAuth")
)
));
использование
private readonly IAuthenticateStrategy _authenticateStrategy;
public AuthenticateController(IAuthenticateStrategy authenticateStrategy)
{
if (authenticateStrategy == null)
throw new ArgumentNullException("authenticateStrategy");
_authenticateStrategy = authenticateStrategy;
}
// login with twitter
public virtual ActionResult Twitter(string user, string pass)
{
bool success =
_authenticateStrategy.Login("TwitterAuth", user, pass);
}
// login with fb
public virtual ActionResult Facebook(string user, string pass)
{
bool success =
_authenticateStrategy.Login("FacebookAuth", user, pass);
}
Единство не будет без вашей помощи. Вы можете указать имя при регистрации типов IAuthenticate:
unityContainer.RegisterType<IAuthenticate, TwitterAuth>("Twitter");
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("Facebook");
Вам больше не нужно напрямую вставлять экземпляр IAuthenticate в свой AuthenticateController. Вы либо получите нужный экземпляр, основываясь на условии прямо из единства (стиль локатора службы):
myContainer.Resolve<IAuthenticate>("Twitter");
или вы введете Фабрику, которая сделает это за вас (если вам нравится строгий стиль DI).