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>>();