I need to create a wrapper class for IAzureMediaServicesClient
which if injected as a scoped service (in a single http request) can return same client object to the callers.
This is the current wrapper code that needs to be fixed.
public class AzureMediaServicesClientProvider : IAzureMediaServicesClientProvider
{
private readonly IConfiguration _configuration;
public AzureMediaServicesClientProvider(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<IAzureMediaServicesClient> GetClient()
{
ServiceClientCredentials credentials = await ApplicationTokenProvider.LoginSilentAsync(
_configuration[ConfigurationConstants.AadTenantId],
_configuration[ConfigurationConstants.AmsAadClientId],
_configuration[ConfigurationConstants.AmsAadSecret]);
return new AzureMediaServicesClient(new Uri(_configuration[ConfigurationConstants.ArmEndpoint]), credentials)
{
SubscriptionId = _configuration[ConfigurationConstants.SubscriptionId],
};
}
}
The class is registered as a Scoped service in DI
public static IServiceCollection AddAzureMediaServiceClient(this IServiceCollection serviceCollection)
{
return serviceCollection.AddScoped<IAzureMediaServicesClientProvider, AzureMediaServicesClientProvider>();
}
and a sample usage in code
public async Task<Job> CreateJobAsync(string transformName, Job job)
{
IAzureMediaServicesClient client = await _azureMediaServicesClientFactory.GetClient();
return await client.Jobs.CreateAsync(_resourceGroupName, _accountName, transformName, job.Name, job);
}
public async Task<Job> GetJobAsync(string transformName, string jobName)
{
IAzureMediaServicesClient client = await _azureMediaServicesClientFactory.GetClient();
return await client.Jobs.GetAsync(_resourceGroupName, _accountName, transformName, jobName);
}
Now the methods GetJobAsync and CreateJobAsync can be used in the same request and currently in such scenario for each of them a new client would be created. How can the provider class be rewritten so that in a single request same client object would be returned ? (I know I could inject it in a higher level and just pass the value to these methods but this is a simplified example and the real world use case would require a lot of refactoring to achieve this).
public async Task TestMethod()
{
var job = await GetJobAsync(...);
// Do some code modifications
await CreateJobAsync(...);
// How can we make sure here that both GetJobAsync and
// CreateJobAsync used the same client AzureMediaServicesClient instance ?
}
Below sample shows the intent but wouldn't be thread safe if I understand correctly ?
public class AzureMediaServicesClientProvider : IAzureMediaServicesClientProvider
{
private readonly IConfiguration _configuration;
private IAzureMediaServicesClient _client;
public AzureMediaServicesClientProvider(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<IAzureMediaServicesClient> GetClient()
{
if (_client == null)
{
ServiceClientCredentials credentials = await ApplicationTokenProvider.LoginSilentAsync(
_configuration[ConfigurationConstants.AadTenantId],
_configuration[ConfigurationConstants.AmsAadClientId],
_configuration[ConfigurationConstants.AmsAadSecret]);
_client = new AzureMediaServicesClient(new Uri(_configuration[ConfigurationConstants.ArmEndpoint]), credentials)
{
SubscriptionId = _configuration[ConfigurationConstants.SubscriptionId],
};
}
return _client;
}
}
CodePudding user response:
You can use the AsyncLazy<T>
from the package Microsoft.VisualStudio.Threading
:
public class AzureMediaServicesClientProvider : IAzureMediaServicesClientProvider
{
private readonly IConfiguration _configuration;
private readonly AsyncLazy<IAzureMediaServicesClient> _lazyClient;
public AzureMediaServicesClientProvider(IConfiguration configuration)
{
_configuration = configuration;
_lazyClient = new AsyncLazy<IAzureMediaServicesClient>(CreateClient);
}
public Task<IAzureMediaServicesClient> GetClient()
{
return _lazyClient.GetValueAsync();
}
private async Task<IAzureMediaServicesClient> CreateClient()
{
ServiceClientCredentials credentials = await ApplicationTokenProvider.LoginSilentAsync(
_configuration[ConfigurationConstants.AadTenantId],
_configuration[ConfigurationConstants.AmsAadClientId],
_configuration[ConfigurationConstants.AmsAadSecret]);
return new AzureMediaServicesClient(new Uri(_configuration[ConfigurationConstants.ArmEndpoint]), credentials)
{
SubscriptionId = _configuration[ConfigurationConstants.SubscriptionId],
};
}
}
AsyncLazy<T>
is thread-safe for all members.