Autofac - Как создать сгенерированный factory с параметрами
Я пытаюсь создать с Autofac 'сгенерированный' factory, который будет разрешать зависимости в режиме реального времени на основе параметра enum.
Учитывая следующие интерфейсы/классы:
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();
}
}
Я хотел бы использовать Autofac для создания своего рода factory, у которого есть метод, который получает один параметр: ConnectionType и возвращает правильный объект соединения.
Я начал со следующих регистраций:
builder.RegisterType<AutoFacConcepts.Engine.Engine>()
.As<IEngine>()
.InstancePerDependency();
builder.RegisterType<SshConnection>()
.As<IConnection>();
builder.RegisterType<TelnetConnection>()
.As<IConnection>();
Затем я продолжал играть с регистрацией TelnetConnection/SshConnection с различными параметрами:
Я не смог найти правильную комбинацию регистраций, которая позволит мне определить сгенерированный делегат factory, который вернет правильный объект соединения (SshConnection для ConnectionType.Ssh и TelnetConnection для ConnectionType.Telnet).
Ответы
Ответ 1
Обновите класс 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();
}
}
В вашей регистрации используйте лямбду, которая захватывает параметр и возвращает правильный экземпляр 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>();
Если для самого соединения требуется зависимость, вы можете вызвать Resolve
в параметре c
, чтобы разрешить его из текущего контекста вызова. Например, new SshConnection(c.Resolve<IDependency>())
.
Ответ 2
Если вам нужно выбрать тип реализации на основе параметра, вам нужно использовать 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();
}
}
И зарегистрируйте свои реализации IConnection
с помощью ключей перечисления:
builder.RegisterType<Engine>()
. As<IEngine>()
.InstancePerDependency();
builder.RegisterType<SshConnection>()
.Keyed<IConnection>(ConnectionType.Ssh);
builder.RegisterType<TelnetConnection>()
.Keyed<IConnection>(ConnectionType.Telnet);
Если вы хотите сохранить свой ConnectionFactory
, вы можете вручную зарегистрировать его, чтобы использовать IIndex<T,B>
внутренне с:
builder.Register<ConnectionFactory>(c =>
{
var context = c.Resolve<IComponentContext>();
return t => context.Resolve<IIndex<ConnectionType, IConnection>>()[t];
});
В этом случае вам все равно нужно зарегистрировать типы IConnection
в качестве ключа, но ваша реализация Engine
может оставаться неизменной.