12

La risoluzione condizionale è l'ultima cosa che non capisco al momento.Unity Injection Unity - Risoluzione condizionale

Diciamo che abbiamo un'interfaccia IAuthenticate:

public interface IAuthenticate{ 
    bool Login(string user, string pass); 
} 

Ora ho due tipi di autenticazione.

Twitter autenticazione

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 
} 

} 

tipi Registrazione in unità di configurazione:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>(); 
unityContainer.RegisterType<IAuthenticate, FacebookAuth>(); 

iniettare oggetti via di nel nostro controller:

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); 
} 

In che modo l'unità saprà esattamente quale oggetto deve risolvere per diversi tipi di autenticazione? Come faccio a risolvere condizionatamente in questo caso?

Ho parlato con un mio amico, e ha spiegato se questa situazione appare è di progettazione sbagliata, ma questo è solo il modello di fabbrica utilizzato.

+0

Il tuo amico potrebbe avere ragione. Osserva attentamente se non stai violando il [Principio di sostituzione di Liskov] (https://en.wikipedia.org/wiki/Liskov_substitution_principle).Se le implementazioni di "IAuthenticate" non sono intercambiabili l'una con l'altra (il che significa che un utente come "AuthenticateController" non funzionerà correttamente se ottiene un'implementazione errata), si sta violando LSP. In questo caso, la soluzione è spesso quella di dare a ciascuna implementazione la propria astrazione. – Steven

risposta

32

Un modo semplice per risolvere questo è con il strategy pattern. Si noti che è possibile aggiungere o rimuovere provider di accesso senza modificare il progetto: è sufficiente modificare la configurazione DI.

Interfacce

public interface IAuthenticate{ 
    bool Login(string user, string pass); 
    bool AppliesTo(string providerName); 
} 

public interface IAuthenticateStrategy 
{ 
    bool Login(string providerName, string user, string pass); 
} 

Autentica Provider

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); 
    } 
} 

Strategia

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); 
    } 
} 

Unità Registrazione

// 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") 
     ) 
    )); 

Utilizzo

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); 
} 
3

Unità senza il vostro aiuto. Si potrebbe fornire un nome al momento della registrazione i tipi di IAuthenticate:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>("Twitter"); 
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("Facebook"); 

Non è più vorrà iniettare direttamente un'istanza IAuthenticate nella vostra AuthenticateController. Potrete sia ottenere l'istanza che si desidera sulla base di una condizione di destra fuori di unità:

myContainer.Resolve<IAuthenticate>("Twitter"); 

o ti inietta una fabbrica che fa per voi (se vi piace un rigoroso stile DI).