2015-10-22 53 views
11

我試圖與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(); 
    } 
} 

我想使用Autofac產生一些工廠有接收一個參數的方法:ConnectionType並返回正確的連接目的。

我開始用下面的註冊:

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

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

我再繼續TelnetConnection的/ SshConnection註冊有不同的選擇玩:

  1. 命名
  2. 鍵控
  3. 元數據

我無法找到正確的註冊組合,這將允許我定義一個生成的工廠委託,它將返回正確的連接對象(用於ConnectionType.Sh的SshConnection和用於ConnectionType.Telnet的TelnetConnection)。

回答

11

更新Engine類採取Func<ConnectionType, IConnection>,而不是委託。 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(); 
    } 
} 

在您的註冊使用,抓住了參數和返回正確的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>(); 

如果本身所需依賴的連接,你可以呼籲c參數Resolve從當前調用上下文解決。例如,new SshConnection(c.Resolve<IDependency>())

+2

使用'IIndex'的目的是爲了避免手動編寫這個大開關的情況,所以這個邏輯可以由Autofac處理。從長遠來看,如果這種類型有多個構造函數參數,這整個代碼可能會很快變得混亂。 – nemesv

+0

如果OP想要保留'ConnectionFactory'委託而不是'Func ',那麼下面的註冊應該可以工作:'builder.Register ((c,p)=> var type = p.Named ( 「connectionType」); 開關(類型) { 情況ConnectionType.Ssh: .... } }) 。作爲();'點在命名參數name'「connectionType」'與委託聲明中的參數匹配。 – nemesv

+0

我當然不介意'IIndex',但是我總是非常熱衷於保持Autofac作爲我的容器! :D –

5

如果您需要根據參數設置爲選擇一個實現類型,你需要使用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(); 
    } 
} 

並註冊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實現可以保持不變。

+0

我嘗試的第一個解決方案,它工作得很好。我的問題是,現在我的應用程序代碼引用了Autofac dll,以便識別IIndex接口。 – Elie

+0

至於第二個選項,我還沒有確定如何使其發揮作用。't'參數不被識別,因此它不會被編譯。我錯過了什麼? – Elie

+0

如果你不想在你的DLL註冊Autofac你需要使用第二種解決方案,我不知道它爲什麼不編譯你。什麼是錯誤拼圖?你也可以嘗試:'builder.Register (c => { } var context = c.Resolve (); ConnectionFactory result =(ConnectionType t)=> context.Resolve >()[t]; 返回結果; });' – nemesv