c# - patron - Unidad de inyección de dependencia-Resolución condicional
patron de inyeccion de dependencias c# (2)
La resolución condicional es lo último que no entiendo en este momento.
Digamos que tenemos una interfaz
IAuthenticate
:
public interface IAuthenticate{
bool Login(string user, string pass);
}
Ahora tengo dos tipos de autenticación.
Autenticación de Twitter
public class TwitterAuth : IAuthenticate
{
bool Login(string user, string pass)
{
//connect to twitter api
}
}
Autenticación de Facebook
public class FacebookAuth: IAuthenticate
{
bool Login(string user, string pass)
{
//connect to fb api
}
}
Registro de tipos en unidad config:
unityContainer.RegisterType<IAuthenticate, TwitterAuth>();
unityContainer.RegisterType<IAuthenticate, FacebookAuth>();
inyectar objetos a través de DI en nuestro controlador:
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);
}
¿Cómo sabrá exactamente la unidad qué objeto tiene que resolver para los diferentes tipos de autenticación? ¿Cómo hago la resolución condicional en este caso?
Hablé con un amigo mío y me explicó que si esta situación parece ser un diseño incorrecto, pero esto es solo un patrón de fábrica utilizado.
La unidad no lo hará sin tu ayuda. Puede proporcionar un nombre cuando registre sus tipos IAuthenticate:
unityContainer.RegisterType<IAuthenticate, TwitterAuth>("Twitter");
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("Facebook");
Ya no querrá inyectar directamente una instancia IAuthenticate en su AuthenticateController. Obtendrá la instancia que desee en función de una condición directamente fuera de la unidad (estilo del localizador de servicios):
myContainer.Resolve<IAuthenticate>("Twitter");
o inyectará una Fábrica que hace esto por usted (si le gusta un estilo DI estricto).
Una forma simple de resolver esto es con el patrón de estrategia . Tenga en cuenta que puede agregar o eliminar proveedores de inicio de sesión sin cambiar el diseño; simplemente necesita cambiar la configuración DI.
Interfaces
public interface IAuthenticate{
bool Login(string user, string pass);
bool AppliesTo(string providerName);
}
public interface IAuthenticateStrategy
{
bool Login(string providerName, string user, string pass);
}
Autenticar proveedores
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);
}
}
Estrategia
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);
}
}
Registro de unidad
// 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")
)
));
Uso
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);
}