2011-03-24 78 views
4

我正在使用Autofac構造函數注入。我需要弄清楚如何將一個對象實例注入到多個構造函數參數中,而無需在容器設置階段顯式解析每個參數。如何使用Autofac注入同一對象的兩個實例?

我有一個複雜的場景,可以通過這種行爲來簡化;下面的例子只是一個簡化的場景,所以我可以證明我正在尋找的行爲。

例子:

說我有這兩個接口,IOpenable和ICloseable:

public interface IOpenable 
{ 
    void Open(); 
} 
public interface ICloseable 
{ 
    void Close(); 
} 

我有一個實現二者的這門類:

public interface IDoor : IOpenable, ICloseable { } 
public class Door : IDoor, IOpenable, ICloseable 
{ 
    void Open() { ... } 
    void Close() { ... } 
} 

我有這個類,它接受一個I​​Openable和ICloseable:

public class DoorHandler : IDoorHandler 
{ 
    public DoorHandler(IOpenable openable, ICloseable closeable) 
    { 
     ... 
    } 
    ... 
} 

問:

是否可以告訴autofac注入相同 Door對象到兩個參數,每當一個IOpenable和ICloseable是相同的構造函數的依賴?

注:我不能做

container.Register<IDoorHandler>(c => { 
    Door d = c.Resolve<IDoor>(); 
    return new DoorHandler(d,d) 
}); 

做我想做的,但要記住,這DoorHandler類只是一個例子。在我的真實代碼中,「DoorHandler」實際上是一個MVC控制器,我正在使用RegisterControllers()註冊它。所以我不能像上面那樣註冊它。此外,有時候依賴關係圖可能會變得過於複雜,並且在任何情況下明確地執行此操作可能會變得非常困難。

我猜我尋找的是能夠做這樣的事情:

container.RegisterType<DoorHandler>().As<IDoorHandler>(); 
container.RegisterType<Door>().As<IDoor>(); 
container.Register<IOpenable>(c => c.ResolveShared<IDoor>();); 
container.Register<ICloseable>(c => c.ResolveShared<IDoor>();); 

哪裏調用c.ResolveShared<T>將全部解決,以相同的T對象,如果要求在多個參數相同的構造函數。

或許:

container.RegisterType<DoorHandler>().As<IDoorHandler>(); 
container.RegisterType<Door>().As<IDoor>().InstancePerDependencyShared(); 
container.Register<IOpenable>(c => c.Resolve<IDoor>();); 
container.Register<ICloseable>(c => c.Resolve<IDoor>();); 

顯然,如果我用了Door對象的InstancePerLifetimeScope什麼的,每個解決門是相同的實例。但我不想這樣,每次創建一個DoorHandler時我都需要一個新的Door實例,並且我希望該Door作爲兩個參數傳遞給DoorHandler構造函數。

+0

Autofac OOB就像你說的,不支持這一點。但是你的'DoorHandle'示例沒有說明*爲什麼*你需要像這樣解析一個類。如果我實現了'DoorHandle',我會讓它採用一個'IDoor'並且用它來完成:)也許如果你描述了真正的問題,我們可以更好地理解並且可能看到其他構造對象模型的方法? – 2011-03-24 22:20:10

回答

3

好棘手的:) ......這裏是一個可能的解決方案推廣「每個構造」分享:

builder.RegisterControllers(asm)   
    .OnPreparing(e => { 
     var spr = new SharedConstructorParameter(
      typeof(IOpenable), 
      typeof(ICloseable)); 
     e.Parameters = new Parameter[]{ spr }.Concat(e.Parameters); 
    }); 

參數需要在OnPreparing()事件成立,因爲SharedConstructorParameter實例將高速緩存每個解析操作的值。

class SharedConstructorParameter : Parameter 
{ 
    object _cachedInstance; 
    Type[] _sharedParameterTypes; 

    public SharedConstructorParameter(params Type[] sharedParameterTypes) 
    { 
     _sharedParameterTypes = sharedParameterTypes; 
    } 

    protected override bool CanSupplyValue(
     ParameterInfo pi, 
     IComponentContext cc, 
     out Func<object> valueCreator) 
    { 
     valueCreator = null; 
     if (!_sharedParameterTypes.Contains(pi.ParameterType)) 
      return false; 

     valueCreator =() => { 
      _cachedInstance = _cachedInstance ?? cc.Resolve(pi.ParameterType); 
      return cachedInstance; 
     }; 
     return true; 
    } 
} 

此致編譯和調試;)

+0

感謝的人,看起來很有希望。我會稍後再測試一下。 – 2011-03-25 13:04:15

+0

有趣的解決方案。看起來不錯。唯一的缺點是你需要知道你想要在構造函數中共享的所有參數類型的集合。不是一個巨大的限制,但不像生命週期範圍的簡單性那樣「自動化」。優點和缺點,優點和缺點。 :) – 2011-03-25 18:48:30

2

與Autofac最接近的今天,它將註冊爲InstancePerLifetimeScope。但是,如果您擁有的特定用例是MVC控制器,那可能就足夠了。

隨着Autofac的ASP.NET集成,每個傳入的HTTP請求都有自己的生命週期範圍內,因此,如果你有這樣的...

var builder = new ContainerBuilder(); 
builder.RegisterControllers(Assembly.GetExecutingAssembly); 
// Under the covers, this is really doing... 
// builder.RegisterType<DoorController>().InstancePerHttpRequest(); 

這InstancePerHttpRequest類似於InstancePerLifetimeScope的延伸。在您的HTTP請求周圍創建一個新的生命週期範圍,並在最後部署。

那麼你也可以註冊你的共享率壽命對象作爲InstancePerHttpRequest:

builder.RegisterType<Door>().As<IDoor>().InstancePerHttpRequest(); 
builder.RegisterType<Openable>().As<IOpenable>(); 
builder.RegisterType<Closeable>().As<ICloseable>(); 
var container = builder.Build(); 
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 

現在當你的控制器解決,IDoor將在兩個IOpenable和ICloseable實例相同的實例。

如果你是一個請求範圍之外,你能做的最好的是一樣的東西:

var builder = new ContainerBuilder(); 
builder.RegisterType<DoorHandler>().As<IDoorHandler>(); 
builder.RegisterType<Door>().As<IDoor>().InstancePerLifetimeScope(); 
builder.RegisterType<Openable>().As<IOpenable>(); 
builder.RegisterType<Closeable>().As<ICloseable>(); 
var container = builder.Build(); 

註冊「共享」項目,如InstancePerLifetimeScope。然後,當你需要解決,你可以這樣做:

using(var lifetime = container.BeginLifetimeScope()) 
{ 
    var dh = lifetime.Resolve<IDoorHandler>(); 
    // The IDoor will be the same in both references here. 
} 

你可以在技術上把參考門處理using語句外,但隨後如果你的IDoor實現是一次性的,他們會相處配置終身使用範圍在使用結束時,所以要小心。

+0

明智的建議 - 永遠不要說永遠不會;)我接受了挑戰,認爲它可能工作,需要另一套眼睛... – 2011-03-25 04:39:46

相關問題