Dependency injection 避免需要异步初始化的类型的所有DI反模式
我有一个类型Dependency injection 避免需要异步初始化的类型的所有DI反模式,dependency-injection,initialization,async-await,simple-injector,abstract-factory,Dependency Injection,Initialization,Async Await,Simple Injector,Abstract Factory,我有一个类型连接,需要异步初始化。此类型的实例由其他几种类型(例如,存储)使用,每种类型也需要异步初始化(静态,而不是每个实例,并且这些初始化还取决于连接)。最后,我的逻辑类型(例如,logic)使用这些存储实例。目前使用的是简单的喷油器 我尝试了几种不同的解决方案,但总有一种反模式存在 显式初始化(时间耦合) 我当前使用的解决方案具有时间耦合反模式: public sealed class Connections { Task InitializeAsync(); } public s
连接
,需要异步初始化。此类型的实例由其他几种类型(例如,存储
)使用,每种类型也需要异步初始化(静态,而不是每个实例,并且这些初始化还取决于连接
)。最后,我的逻辑类型(例如,logic
)使用这些存储实例。目前使用的是简单的喷油器
我尝试了几种不同的解决方案,但总有一种反模式存在
显式初始化(时间耦合) 我当前使用的解决方案具有时间耦合反模式:
public sealed class Connections
{
Task InitializeAsync();
}
public sealed class Storage : IStorage
{
public Storage(Connections connections);
public static Task InitializeAsync(Connections connections);
}
public sealed class Logic
{
public Logic(IStorage storage);
}
public static class GlobalConfig
{
public static async Task EnsureInitialized()
{
var connections = Container.GetInstance<Connections>();
await connections.InitializeAsync();
await Storage.InitializeAsync(connections);
}
}
这里的问题是我们回到了时间耦合,这一次分散在整个系统中。此外,这种方法要求所有公共成员都是异步方法
因此,这里有两种DI设计观点存在分歧:
- 消费者希望能够注入准备好使用的实例
- DI容器用力推动
更新:AzureConnections的完整签名(上面称为
连接
):
虽然我很确定以下内容不是你想要的,但你能解释一下为什么它不能解决你的问题吗
public sealed class AzureConnections
{
private readonly Task<CloudStorageAccount> storage;
public AzureConnections()
{
this.storage = Task.Factory.StartNew(InitializeStorageAccount);
// Repeat for other cloud
}
private static CloudStorageAccount InitializeStorageAccount()
{
// Do any required initialization here...
return new CloudStorageAccount( /* Constructor arguments... */ );
}
public CloudStorageAccount CloudStorageAccount
{
get { return this.storage.Result; }
}
}
公共密封类AzureConnections
{
专用只读任务存储;
公共AzureConnections()
{
this.storage=Task.Factory.StartNew(InitializeStorageAccount);
//对其他云重复此操作
}
私有静态CloudStorageAccount初始值estorageAccount()
{
//在此处执行任何必需的初始化。。。
返回新的CloudStorageAccount(/*构造函数参数…*/);
}
公共CloudStorageAccount CloudStorageAccount
{
获取{返回this.storage.Result;}
}
}
为了保持设计清晰,我只实现了一个云属性,但是其他两个可以用类似的方式实现
AzureConnections
构造函数不会阻塞,即使初始化各种云对象需要很长时间
另一方面,它将开始工作,并且由于.NET任务的行为类似于承诺,因此当您第一次尝试访问该值时(使用结果
),它将返回由InitializeStrageAccount
生成的值
我得到的强烈印象是,这不是你想要的,但由于我不明白你想解决什么问题,我想我会留下这个答案,这样至少我们会有一些事情要讨论。你的问题,而且,是一个典型的问题。这是典型的,有两个原因:
- 启动初始化是同步的。框架(如ASP.NET Core)通常不支持启动阶段的异步初始化
- 初始化通常需要根据请求及时完成,而不是根据应用程序提前完成。通常,需要初始化的组件的生命周期很短,这意味着我们通常在第一次使用时初始化这样的实例(换句话说:及时)
public sealed class Connections
{
private Task InitializeAsync(); // Use Lazy internally
// Used to be a property BobConnection
public X GetBobConnectionAsync()
{
await InitializeAsync();
return BobConnection;
}
}
public sealed class Storage : IStorage
{
public Storage(Connections connections);
private static Task InitializeAsync(Connections connections); // Use Lazy internally
public async Task<Y> IStorage.GetAsync()
{
await InitializeAsync(_connections);
var connection = await _connections.GetBobConnectionAsync();
return await connection.GetYAsync();
}
}
public sealed class Logic
{
public Logic(IStorage storage);
public async Task<Y> GetAsync()
{
return await _storage.GetAsync();
}
}
public sealed class AzureConnections
{
public AzureConnections();
public CloudStorageAccount CloudStorageAccount { get; }
public CloudBlobClient CloudBlobClient { get; }
public CloudTableClient CloudTableClient { get; }
public async Task InitializeAsync();
}
public sealed class AzureConnections
{
private readonly Task<CloudStorageAccount> storage;
public AzureConnections()
{
this.storage = Task.Factory.StartNew(InitializeStorageAccount);
// Repeat for other cloud
}
private static CloudStorageAccount InitializeStorageAccount()
{
// Do any required initialization here...
return new CloudStorageAccount( /* Constructor arguments... */ );
}
public CloudStorageAccount CloudStorageAccount
{
get { return this.storage.Result; }
}
}
services.AddSingleton<IWebProxy>((sp) =>
{
//Notice the GetService outside the Task. It was locking when it was inside
var data = sp.GetService<IData>();
return Task.Run(async () =>
{
try
{
var credentials = await data.GetProxyCredentialsAsync();
if (credentials != null)
{
return new WebHookProxy(credentials);
}
else
{
return (IWebProxy)null;
}
}
catch(Exception ex)
{
throw;
}
}).Result; //Back to sync
});