簡體   English   中英

依賴於具有異步解析方法的工廠的 DI 存儲庫

[英]DI repository which depends on factory with an asynchronous resolve method

我有一個UserRepository依賴於IDynamoDbClientFactory 問題是IDynamoDbClientFactory只有一種方法並且是異步的。 ServiceCollections DI 框架不允許我使用異步提供程序。 我不允許更改DynamoDbClientFactory ,因為它在外部庫中。

我如何以比我在下面使用.GetAwaiter().GetResult()所做的更好的方式處理這個問題?

services.AddSingleton<IDynamoDbClientFactory, DynamoDbClientFactory>();

services.AddSingleton<IUserRepository>(provider =>
{
    var dbClientFactory = provider.GetRequiredService<IDynamoDbClientFactory>();
    var dynamoDb = dbClientFactory.GetClientAsync().GetAwaiter().GetResult();
    return new UserRepository(dynamoDb);
});

我發現了這個類似的問題,它建議使用適配器來隱藏應用程序代碼中的異步操作。

為了簡單起見,我刪除了不重要的代碼。

public interface IDynamoDbClientFactory
{
    Task<IAmazonDynamoDB> GetClientAsync();
}

public sealed class DynamoDbClientFactory : IDynamoDbClientFactory
{
    private readonly IConfiguration _configuration;
    private IAmazonDynamoDB? _client;
    private DateTime _clientTimeout;

    public DynamoDbClientFactory(IConfiguration configuration)
    {
        _configuration = configuration;
        _clientTimeout = DateTime.Now;
    }

    public async Task<IAmazonDynamoDB> GetClientAsync()
    {
        var cutoff = _clientTimeout - TimeSpan.FromMinutes(5);

        if (_client != null && cutoff > DateTime.Now)
        {
            return _client;
        }

        var assumeRoleArn = _configuration.GetValue<string>("AWS:AssumeRole");

        if (assumeRoleArn == null)
        {
            _client = new AmazonDynamoDBClient();
            _clientTimeout = DateTime.MaxValue;
        }
        else
        {
            var credentials = await GetCredentials(assumeRoleArn);
            _client = new AmazonDynamoDBClient(credentials);
            _clientTimeout = credentials!.Expiration;
        }

        return _client;
    }

    private async Task<Credentials?> GetCredentials(string roleArn)
    {
        using var client = new AmazonSecurityTokenServiceClient();
        var response = await client.AssumeRoleAsync(new AssumeRoleRequest
        {
            RoleArn = roleArn,
            RoleSessionName = "configManagerApi",
        });

        if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
        {
            return response.Credentials;
        }

        throw new ApplicationException($"Could not assume role {roleArn}");
    }
}
public sealed class UserRepository : IUserRepository
{
    private readonly IAmazonDynamoDB _dynamoDb;

    public UserRepository(IAmazonDynamoDB dynamoDb)
    {
        _dynamoDb = dynamoDb;
    }

    public async Task<UserDto?> GetAsync(string hashKey, string sortKey)
    {
        ...
    }

    public async Task<bool> CreateAsync(UserDto userDto)
    {
        ...
    }

    public async Task<bool> UpdateAsync(UserDto userDto)
    {
        ...
    }

    public async Task<bool> DeleteAsync(string hashKey, string sortKey)
    {
        ...
    }
}

以下是未使用的。

// The adapter hides the details about GetClientAsync from the application code.
// It wraps the creation and connection of `MyClient` in a `Lazy<T>`,
// which allows the client to be connected just once, independently of in which order the `GetClientAsync`
// method is called, and how many times.
public class DynamoDbClientAdapter : IDisposable
{
    private readonly Lazy<Task<IDynamoDbClientFactory>> _factories;

    public DynamoDbClientAdapter(IConfiguration configuration)
    {
        _factories = new Lazy<Task<IDynamoDbClientFactory>>(async () =>
        {
            var client = new DynamoDbClientFactory(configuration);
            await client.GetClientAsync();
            return client;
        });
    }

    public void Dispose()
    {
        if (_factories.IsValueCreated)
        {
            _factories.Value.Dispose();
        }
    }
}

嘗試以下操作:

services.AddSingleton<IDynamoDbClientFactory, DynamoDbClientFactory>();
services.AddScoped<IUserRepository, DynamicClientUserRepositoryAdapter>();

其中DynamicClientUserRepositoryAdapterComposition Root中的一個適配器,它根據其所需的IAmazonDynamoDB UserRepository

public class DynamicClientUserRepositoryAdapter : IUserRepository
{
    private readonly IDynamoDbClientFactory factory;
    private IUserRepository repository;

    public DynamicClientUserRepositoryAdapter(IDynamoDbClientFactory factory) =>
        this.factory = factory;

    private async Task<IUserRepository> GetRepositoryAsync()
    {
        if (this.repository is null)
        {
            var client = await this.factory.GetClientAsync();
            this.repository = new UserRepository(client);
        }
        
        return this.repository;
    }
    
    public async Task<UserDto?> GetAsync(string hashKey, string sortKey)
    {
        var repository = await this.GetRepositoryAsync();
        return await repository.GetAsync(hashKey, sortKey);
    }

    public async Task<bool> CreateAsync(UserDto userDto)
    {
        var repository = await this.GetRepositoryAsync();
        return await repository.CreateAsync(userDto);
    }

    public async Task<bool> UpdateAsync(UserDto userDto)
    {
        var repository = await this.GetRepositoryAsync();
        return await repository.UpdateAsync(userDto);
    }

    public async Task<bool> DeleteAsync(string hashKey, string sortKey)
    {
        var repository = await this.GetRepositoryAsync();
        return await repository.DeleteAsync(hashKey, sortKey);
    }
}

注意:我假設IAmazonDynamoDB不是線程安全的,這就是我將DynamicClientUserRepositoryAdapter注冊為作用域的原因。

您在問題中已經提到的答案中解釋了為什么設計這樣的東西的合理性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM