Autofac - 如何使用参数创建生成的工厂

Eli*_*lie 17 .net c# autofac

我正在尝试使用Autofac创建一个"生成"工厂,它将基于枚举参数实时解析依赖关系.

给定以下接口/类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我想使用Autofac生成某种工厂,它有一个接收一个参数的方法:ConnectionType并返回正确的连接对象.

我从以下注册开始:

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

builder.RegisterType<SshConnection>()
    .As<IConnection>();
builder.RegisterType<TelnetConnection>()
    .As<IConnection>();
Run Code Online (Sandbox Code Playgroud)

然后我继续使用不同的选项来玩TelnetConnection/SshConnection注册:

  1. 命名
  2. 键控
  3. 元数据

我找不到正确的注册组合,这些注册允许我定义一个生成的工厂委托,它将返回正确的连接对象(ConnectionType.Ssh的SshConnection和ConnectionType.Telnet的TelnetConnection).

Ale*_*ves 18

更新Engine类以Func<ConnectionType, IConnection>取代委托.Autofac 支持动态创建委托工厂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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的注册中使用lambda来抓取参数并返回正确的IConnection实例.

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>();
Run Code Online (Sandbox Code Playgroud)

如果连接本身需要依赖你可以调用Resolvec参数从当前调用上下文解决.例如,new SshConnection(c.Resolve<IDependency>()).

  • 使用`IIndex`的要点是避免手动编写这个大开关盒,这样这个逻辑可以由Autofac处理.从长远来看,如果这个类型有多个构造函数参数,那么整个代码可能会很快变得混乱. (4认同)

nem*_*esv 8

如果需要根据参数选择实现类型,则需要使用IIndex<T,B>隐式关系类型:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

IConnection使用枚举键注册您的实现:

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

builder.RegisterType<SshConnection>()
    .Keyed<IConnection>(ConnectionType.Ssh);
builder.RegisterType<TelnetConnection>()
    .Keyed<IConnection>(ConnectionType.Telnet);
Run Code Online (Sandbox Code Playgroud)

如果你想留意ConnectionFactory你可以手动注册它以使用IIndex<T,B>内部:

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

在这种情况下,您仍然需要将IConnection类型注册为键控,但您的Engine实现可以保持不变.