问题
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:
- Named
- Keyed
- 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