2015-10-22 23 views
11

Sto provando a creare con Autofac uno factory "generato" che risolverà le dipendenze in tempo reale sulla base di un parametro enum.Autofac - Come creare un factory generato con parametri

Date le seguenti interfacce/classi:

public delegate IConnection ConnectionFactory(ConnectionType connectionType); 

public enum ConnectionType 
{ 
    Telnet, 
    Ssh 
} 

public interface IConnection 
{ 
    bool Open(); 
} 

public class SshConnection : ConnectionBase, IConnection 
{ 
    public bool Open() 
    { 
     return false; 
    } 
} 

public class TelnetConnection : ConnectionBase, IConnection 
{ 
    public bool Open() 
    { 
     return true; 
    } 
} 

public interface IEngine 
{ 
    string Process(ConnectionType connectionType); 
} 

public class Engine : IEngine 
{ 
    private ConnectionFactory _connectionFactory; 

    public Engine(ConnectionFactory connectionFactory) 
    { 
     _connectionFactory = connectionFactory; 
    } 

    public string Process(ConnectionType connectionType) 
    { 
     var connection = _connectionFactory(connectionType); 

     return connection.Open().ToString(); 
    } 
} 

Mi piacerebbe usare Autofac per generare una sorta di fabbrica che ha un metodo che riceve un parametro: ConnectionType e restituisce il corretto collegamento oggetto.

Ho iniziato con le seguenti iscrizioni:

builder.RegisterType<AutoFacConcepts.Engine.Engine>() 
    .As<IEngine>() 
    .InstancePerDependency(); 

builder.RegisterType<SshConnection>() 
    .As<IConnection>(); 
builder.RegisterType<TelnetConnection>() 
    .As<IConnection>(); 

Ho poi continuato a giocare con le iscrizioni TelnetConnection/SshConnection con diverse opzioni:

  1. Chiamato
  2. con chiave
  3. metadati

Non sono riuscito a trovare la combinazione corretta delle registrazioni che consentirà di definire un delegato di fabbrica generato che restituirà l'oggetto di connessione corretto (SshConnection per ConnectionType.Ssh e TelnetConnection per ConnectionType.Telnet).

risposta

11

Aggiornare la classe Engine per prendere un Func<ConnectionType, IConnection> invece del delegato. Autofac supports creating delegate factories on the fly via Func<T>.

public class Engine : IEngine 
{ 
    private Func<ConnectionType, IConnection> _connectionFactory; 

    public Engine(Func<ConnectionType, IConnection> connectionFactory) 
    { 
     _connectionFactory = connectionFactory; 
    } 

    public string Process(ConnectionType connectionType) 
    { 
     var connection = _connectionFactory(connectionType); 

     return connection.Open().ToString(); 
    } 
} 

Nella tua registrazione utilizzare una lambda che afferra il parametro e restituisce il corretto IConnection istanza.

builder.Register<IConnection>((c, p) => 
{ 
    var type = p.TypedAs<ConnectionType>(); 
    switch (type) 
    { 
     case ConnectionType.Ssh: 
      return new SshConnection(); 
     case ConnectionType.Telnet: 
      return new TelnetConnection(); 
     default: 
      throw new ArgumentException("Invalid connection type"); 
    } 
}) 
.As<IConnection>(); 

Se la connessione in sé necessaria una dipendenza si potrebbe chiamare Resolve sul parametro c risolverlo dal contesto chiamata in corso. Ad esempio, new SshConnection(c.Resolve<IDependency>()).

+2

Il punto nell'utilizzo di' IIndex' è evitare la scrittura manuale di questi casi di switch di grandi dimensioni in modo che questa logica possa essere gestita da Autofac. Nel lungo periodo, se questo tipo ha più argomenti del costruttore, questo intero codice potrebbe diventare molto veloce. – nemesv

+0

Se l'OP vuole mantenere il delegato 'ConnectionFactory' invece di' Func 'allora la seguente registrazione dovrebbe funzionare:' builder.Register ((c, p) => { var type = p.Named ("ConnectionType"); interruttore (tipo) { caso ConnectionType.Ssh: .... }} ) .Come (); 'il punto è il nome del parametro nome '" connectionType "' ha la corrispondenza del parametro nella dichiarazione dei delegati. – nemesv

+0

Certamente non mi dispiace 'IIndex', ma di nuovo sono sempre molto interessato a mantenere Autofac come contenitore! : D –

5

Se è necessario selezionare un tipo di implementazione basata su paramter è necessario utilizzare il IIndex<T,B> implicit relation type:

public class Engine : IEngine 
{ 
    private IIndex<ConnectionType, IConnection> _connectionFactory; 

    public Engine(IIndex<ConnectionType, IConnection> connectionFactory) 
    { 
     _connectionFactory = connectionFactory; 
    } 

    public string Process(ConnectionType connectionType) 
    { 
     var connection = _connectionFactory[connectionType]; 

     return connection.Open().ToString(); 
    } 
} 

e registrare il IConnection implementazioni con i tasti enum:

builder.RegisterType<Engine>() 
.   As<IEngine>() 
    .InstancePerDependency(); 

builder.RegisterType<SshConnection>() 
    .Keyed<IConnection>(ConnectionType.Ssh); 
builder.RegisterType<TelnetConnection>() 
    .Keyed<IConnection>(ConnectionType.Telnet); 

Se si desidera tieni il tuo ConnectionFactory puoi registrarlo manualmente per utilizzare uno IIndex<T,B> interno con:

builder.Register<ConnectionFactory>(c => 
{ 
    var context = c.Resolve<IComponentContext>(); 
    return t => context.Resolve<IIndex<ConnectionType, IConnection>>()[t]; 
}); 

In questo caso è ancora necessario registrare i tipi IConnection come digitati ma l'implementazione Engine può rimanere invariata.

+0

Ho provato la prima soluzione e funziona perfettamente. Il mio problema è che ora il mio codice app ha un riferimento alla dll di Autofac in modo che l'interfaccia IIndex sia riconosciuta. – Elie

+0

Per quanto riguarda la seconda opzione, non sono ancora sicuro su come farlo funzionare.il parametro 't' non è riconosciuto e quindi non sta compilando. Mi sto perdendo qualcosa? – Elie

+0

Se non si desidera registrare Autofac nella propria DLL, è necessario utilizzare la seconda soluzione, non so perché non si sta compilando per voi .. qual è il messaggio di errore? Puoi anche provarlo con: 'builder.Register (c => { var context = c.Resolve (); ConnectionFactory result = (ConnectionType t) => context.Resolve >() [t]; risultato restituito;}); ' – nemesv