2015-11-04 130 views
3

TL; DR:Autofac是否支持類似於AutoFixture的fixture.Get()機制?Autofac:註冊異步工廠方法

我使用Autofac和需要異步調用工廠方法看起來是這樣的:

class AppModel 
{ 
    public static async Task<AppModel> CreateAsync(IDependency x, IDependency2 y) 
    { ... } 
} 

什麼是我要執行這樣的方法並有論據Autofac提供最簡單的方法?也就是說,我希望能夠做一些事情,如:

Task<AppModel> creationTask = <some autofaccery>(AppModel.CreateAsync); 
var appModel = await creationTask(); 

其中<some autofaccery>代表與ContainerBuilder和/或​​和/或某種形式的generated Delegates或類似交互的某種機制在本質上是簡潔和隔離我明確指定Factory Method的參數。即,我想避免明確地解決每個參數[和/或必須更新他們的依賴性變】像我這樣ATM:

var appModel = await AppModel.CreateAsync( 
    container.Resolve<IDependency>(), 
    container.Resolve<IDependency2>()); 

我在基礎設施組件境內,靠近組成的根和可能潛在地以編程方式定義組件註冊和/或做其他應該限制在那裏的麻煩。我不介意反思,因爲它只被調用一次。

什麼是關鍵是我確實需要從Task發出的任何Exception觀察。

Task<T>的是一個紅色鯡魚在很大的程度,但問題是,以下定義的同步工廠方法以及具有Autofac通過工作不會飛(至少不直接地)的正常模式,即,I可以「T只是將其更改爲:

 public static AppModel CreateAsync(IDependency x, IDependency2 y) 
    { ... } 

我也想避免兩相初始化 - 我不需要的對象,直到它被初始化爲可用。

回答

0

(由TL的啓發; DR我在頂部加)

你可以實現一個ResolveAsync家庭的方法: -

public static Task<T> ResolveAsync<T1, T>(Func<T1, Task<T>> func) 
{ 
    return func(_container.Resolve<T1>()); 
} 

public static Task<T> ResolveAsync<T1, T2, T>(Func<T1, T2, Task<T>> func) 
{ 
    return func(_container.Resolve<T1>(), _container.Resolve<T2>()); 
} 

public static Task<T> ResolveAsync<T1, T2, T3, T>(Func<T1, T2, T3, Task<T>> func) 
{ 
    return func(_container.Resolve<T1>(), _container.Resolve<T2>(), _container.Resolve<T3>()); 
} 

這讓我做的事情:

var appModel = await ResolveAsync<IDependency,IDependency2>(AppModel.CreateAsync); 

或者顯然這些可以變成擴展方法: -

var appModel = await container.ResolveAsync<IDependency,IDependency2>(AppModel.CreateAsync); 

工作在一個包裝上,以允許使用C#的原始類型推斷:( 我將節約我的能量,而不是再花時間去實現language/async paradigm not built for the job中的適當解決方案。

+0

爲什麼效益做到這一點的解決方案使用標準的解決方法給您禁忌? – jgauffin

+0

@jguaffin爲了澄清一點,我不希望有任何明確的註冊,依靠依靠[代表工廠](http://docs.autofac.org/en/latest/advanced/delegate-factories.html)和我正在尋找類似的處理'任務'方面的[關係類型](http://nblumhardt.com/2010/01/the-relationship-zoo/) - 但美中不足的是, 't想要做'異步' –

+0

@jguaffin看到[我的第三個答案](http://stackoverflow.com/a/37390850/11635)的東西,實現了封裝,我希望從消費者的角度來看,沒有需要在Autofac中進行明確的註冊。 –

0

除了需要一套醜陋的擴展方法,我的第一個答案泄露了工廠的簽名到調用模塊中,需要引用不直接需要的名稱空間。爲了隱藏這一點,可以使用相同的方案,但封裝依賴於AsyncFactory類設置: -

class AppModel 
{ 
    public class AsyncFactory 
    { 
      public AsyncFactory(IDependency x, IDependency2 y) 
      { 
       CreateAsync = async() => 
        new AppModel(x.CalculateA(await y.CalculateB())); 
      } 
      public Func<Task<AppModel> CreateAsync { get;} 
    } 
    private AppModel(A a) { ... } 
} 

然後調用者可以使用統一的機制,使用工廠: -

var appModel = await container.Resolve<AppModel.Factory>().CreateAsync(); 

(注意沒有參數類型因此增加進一步依賴於AsyncFactory的重述不會觸發的連鎖變化到調用代碼)

0

濫用LazyTask<T>,一個可以改變一個CreateAsync方法弄成Autofac 可以解決: - 一個類型[從LazyTask<T>衍生],它看起來像這樣:

class AppModel 
{ 
    public class AsyncFactory : LazyTask<AppModel> 
    { 
      public AsyncFactory(IDependency x, IDependency2 y) : base(async() => 
       new AppModel(x.CalculateA(await y.CalculateB()))) 
      {} 
    } 
    private AppModel(A a) { ... } 
} 

這可消耗如下: -

var appModel = await container.Resolve<AppModel.AsyncFactory>(); 

不接受這一點,因爲我仍然覺得有一個機會讓這個更清晰 - 即,如果Autofac將Task<T> CreateAsync方法應用於如下方法: -

class AppModel 
{ 
    public async Task<AppModel> CreateAsync(IDependency x, IDependency2 y) => 
     new AppModel(x.CalculateA(await y.CalculateB())); 
} 

自動註冊爲類型Task<T>,允許一個消耗如下,而不依賴於我Task包裝類型: -

var appModel = await container.Resolve<Task<AppModel>>();