Autofac:注册异步工厂方法

时间:2015-11-04 14:44:10

标签: c# asynchronous dependency-injection autofac factory-method

TL; DR:Autofac是否支持类似AutoFixture fixture.Get()机制的内容?

我使用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和/或IContainer和/或某种形式的generated Delegates或类似形式进行交互的一些机制,这些机制本质上是简洁的并且将我与明确指定工厂方法的参数。即,我想避免必须明确解决每个参数[和/或必须更新它们作为依赖项更改],就像我做atm:

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

我位于基础架构组件区域,靠近组合根,并且可能以编程方式定义组件注册和/或做其他应该限制在那里的肮脏。我不介意反思,因为它只被调用一次。

重要的是,我确实需要Exception发出的任何Task来发现。

Task<T>在很大程度上是一个红色的鲱鱼,但重点是遵循定义同步工厂方法的正常模式并让Autofac通过它不会飞(至少不直接) ),即我不能将其改为:

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

我也想避免两阶段初始化 - 我不需要该对象在初始化之前可用。

3 个答案:

答案 0 :(得分:0)

(灵感来自TL; DR我在顶部添加)

您可以实施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);

或者显然这些可以变成扩展方法: -

<div class='draggable' id='something'>
    <ul>
        <li id='frank'>Frank</li>
        <li id='Joe'>Joe</li>
    </ul>
</div>
<br>
    <br>
        <div class='draggable' id='somethingElse'>
            <div id='yes'>Yes</div>
            <div id='no'>no</div>
        </div>

处理一个包装器,允许使用C#的原始类型推理:(我将节省我的精力,而不是再花费更多时间来实现一个正确的解决方案在language/async paradigm not built for the job

答案 1 :(得分: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添加更多依赖项不会触发对调用代码的连锁更改)

答案 2 :(得分:0)

滥用LazyTask<T>,可以将LazyTask<T>方法转换为Autofac 可以解析的内容: - 类型[源自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>();

可以按如下方式使用: -

Task<T> CreateAsync

不接受这一点,因为我仍然觉得有机会更清楚 - 即如果Autofac要对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>>(); 包装类型: -

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>  
    <link href="http://www.google.com/uds/modules/elements/transliteration/api.css" type="text/css" rel="stylesheet"/>

    <script type="text/javascript">
      // Load the Google Transliteration API
      google.load("elements", "1", {
            packages: "transliteration"
          });
      function onLoad() {
        var options = {
            sourceLanguage:
                google.elements.transliteration.LanguageCode.ENGLISH,
            destinationLanguage:
                [google.elements.transliteration.LanguageCode.HINDI],
            shortcutKey: 'ctrl+g',
            transliterationEnabled: true
        };

        // Create an instance on TransliterationControl with the required
        // options.
        var control =
            new google.elements.transliteration.TransliterationControl(options);

        // Enable transliteration in the textbox with id
        // 'transliterateTextarea'.
        control.makeTransliteratable(['transliterateTextarea']);
      }
      google.setOnLoadCallback(onLoad);
    </script>
  </head>
  <body>

    Type in Hindi (Press Ctrl+g to toggle between English and Hindi)<br>
    <a href="#" onclick="document.getElementById('Wrapper').style.display = 'block'; return false;">show textarea</a>
    <div id="Wrapper" style="display:none"> <h2> Textarea </h2>
    <textarea id="transliterateTextarea" style="width:600px;height:200px"></textarea>
    <textarea id="transliterateTextarea2" style="width:600px;height:200px"></textarea>
    </div>
  </body>
</html>