Autofac - How to create a generated factory with parameters

假如想象 提交于 2019-12-20 09:54:21

问题


I am trying to create with Autofac a 'generated' factory that will resolve dependencies in real-time based on an enum parameter.

Given the following interfaces/classes:

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

I'd like to use Autofac to generate some sort of factory that has a method that receives one parameter: ConnectionType and returns the correct connection object.

I started with the following registrations:

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

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

I then continued to play with the TelnetConnection/SshConnection registrations with different options:

  1. Named
  2. Keyed
  3. Metadata

I couldn't find the correct combination of the registrations that will allow me to define a generated factory delegate that will return the correct connection object (SshConnection for ConnectionType.Ssh and TelnetConnection for ConnectionType.Telnet).


回答1:


Update the Engine class to take a Func<ConnectionType, IConnection> instead of the delegate. 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();
    }
}

In your registration use a lambda that grabs up the parameter and returns the correct IConnection instance.

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

If the connection itself required a dependency you could call Resolve on the c parameter to resolve it from the current call context. For example, new SshConnection(c.Resolve<IDependency>()).




回答2:


If you need to select an implementation type based on paramter you need to use the 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();
    }
}

And register your IConnection implementations with the enum keys:

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

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

If you want to keep your ConnectionFactory you can manually register it to use an IIndex<T,B> internally with:

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

In this case you still need to register your IConnection types as keyed but your Engine implementation can remain the same.



来源:https://stackoverflow.com/questions/33288872/autofac-how-to-create-a-generated-factory-with-parameters

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!