web-dev-qa-db-fra.com

Autofac - Comment créer une usine générée avec des paramètres

J'essaie de créer avec Autofac une usine "générée" qui résoudra les dépendances en temps réel en fonction d'un paramètre enum.

Étant donné les interfaces/classes suivantes :

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

Je voudrais utiliser Autofac pour générer une sorte d'usine qui a une méthode qui reçoit un paramètre: ConnectionType et renvoie l'objet de connexion correct.

J'ai commencé avec les enregistrements suivants:

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

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

J'ai ensuite continué à jouer avec les enregistrements TelnetConnection/SshConnection avec différentes options:

  1. Nommé
  2. À clé
  3. Métadonnées

Je n'ai pas trouvé la combinaison correcte des enregistrements qui me permettra de définir un délégué d'usine généré qui renverra l'objet de connexion correct (SshConnection pour ConnectionType.Ssh et TelnetConnection pour ConnectionType.Telnet).

18
Elie

Mettez à jour la classe Engine pour prendre un Func<ConnectionType, IConnection> À la place du délégué. Autofac prend en charge la création d'usines de délégués à la volée 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();
    }
}

Dans votre enregistrement, utilisez un lambda qui récupère le paramètre et renvoie l'instance IConnection correcte.

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

Si la connexion elle-même nécessite une dépendance, vous pouvez appeler Resolve sur le paramètre c pour la résoudre à partir du contexte d'appel actuel. Par exemple, new SshConnection(c.Resolve<IDependency>()).

20
Alex Meyer-Gleaves

Si vous devez sélectionner un type d'implémentation basé sur un paramètre, vous devez utiliser IIndex<T,B> type de relation implicite :

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

Et enregistrez vos implémentations IConnection avec les clés enum:

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

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

Si vous souhaitez conserver votre ConnectionFactory, vous pouvez l'enregistrer manuellement pour utiliser un IIndex<T,B> en interne avec:

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

Dans ce cas, vous devez toujours enregistrer vos types IConnection en tant que clés, mais votre implémentation Engine peut rester la même.

9
nemesv