同一存储的依赖注入多个接口(具有不同的connectionString)
Posted
技术标签:
【中文标题】同一存储的依赖注入多个接口(具有不同的connectionString)【英文标题】:dependency injection multiple interface for the same storage (with different connectionString) 【发布时间】:2021-09-25 13:51:13 【问题描述】:我想在 Startup.cs 中添加两个或更多(取决于我要添加到我的应用程序的 azure 存储容器的数量)服务
我的 appsettings.json:
"AzureBlobStorageConfiguration":
"Storages":
"Storage1":
"StorageName": "Storage1",
"ConnString": "connString",
"AzureBlobContainerName": "containerName"
,
"Storage2":
"StorageName": "Storage2",
"ConnString": "connString",
"AzureBlobContainerName": "containerName"
,
"Storage3":
"StorageName": "Storage3",
"ConnString": "connString",
"AzureBlobContainerName": "containerName"
接下来在 Startup.cs 中使用方法添加服务:
public static IServiceCollection AddAzureStorage1(this IServiceCollection services, IConfiguration configuration)
var options = new ABlobStorageConfigurationOptionsDTO();
configuration.GetSection("AzureBlobStorageConfiguration").GetSection("Storages").GetSection("Storage1").Bind(options);
services.AddTransient<IAzureBlobStorage1, AzureBlobStorage1>(isp =>
var client = new BlobServiceClient(options.ConnString);
var container = client.GetBlobContainerClient(options.AzureBlobContainerName);
var containerName = options.AzureBlobContainerName;
var storageName = options.StorageName;
return new AzureBlobStorage1(container, containerName, storageName);
);
return services;
我的 IAzureBlobStorage1 看起来像:
public interface IAzureBlobStorage1
string AzureBlobContainerName get;
string StorageName get;
public Task<Stream> DownloadStreamAsyns(string fileName);
public Task Upload(string fileId, Stream stream);
和 AzureBlobStorage1:
public class AzureBlobStorage1 : IAzureBlobStorage1
private BlobContainerClient _client;
private string _containerName;
private string _storageName;
public string StorageName => _storageName;
public string AzureBlobContainerName => _containerName;
public AzureBlobStorage1(BlobContainerClient client, string containerName, string storageName)
_client = client;
_containerName = containerName;
_storageName = storageName;
public async Task<Stream> DownloadStreamAsyns(string fileName)
return await _client.GetBlobClient(fileName).OpenReadAsync();
public async Task Upload(string fileId, Stream stream)
await _client.GetBlobClient(fileId).UploadAsync(stream);
之后我可以在我的构造函数控制器类中注入接口:
public Controller(IAzureBlobStorage1 azureStorage)
_azureStorage1 = azureStorage;
但如果我想添加许多存储(我在 appsetings.json 中有 3 个),我必须:
创建接口 IAzureBlobStorage2(看起来像 IAzureBlobStorage1 - 只是名称更改)
创建类 AzureBlobStorage2(看起来与 AzureBlobStorage1 相同 - 仅更改名称)
更改类名的复制粘贴方法
public static IServiceCollection AddAzureStorage2(this IServiceCollection services, IConfiguration configuration)
var options = new ABlobStorageConfigurationOptionsDTO();
configuration.GetSection("AzureBlobStorageConfiguration").GetSection("Storages").GetSection("Storage2").Bind(options);
services.AddTransient<IAzureBlobStorage2, AzureBlobStorage2>(isp =>
var client = new BlobServiceClient(options.ConnString);
var container = client.GetBlobContainerClient(options.AzureBlobContainerName);
var containerName = options.AzureBlobContainerName;
var storageName = options.StorageName;
return new AzureBlobStorage2(container, containerName, storageName);
);
return services;
现在我可以在控制器中通过
public Controller(IAzureBlobStorage2 azureStorage)
_azureStorage2 = azureStorage;
如果我想添加我的第三个存储,我需要第三次复制粘贴我的代码。
对我来说,这个解决方案看起来很糟糕,我正在考虑如何解决它并让我的代码干净。
【问题讨论】:
创建一个包含 blob 存储对象列表的类,在设置中使用可用的内容对其进行配置,然后注入该类。用户将能够从单个持有者中提取单独的存储。 【参考方案1】:不确定这是否是最佳实践,但您可以设计一个命名服务提供商,也许吧?要么,或者你可以只是一个通用参数来区分它们,但是这个通用参数除了作为一种区分方式之外没有多大意义..
无论如何,这是一个使用某种命名提供程序的非常基本的实现?:
public interface INamedService
string Identifier get;
public interface IAzureBlobStorage : INamedService
string AzureBlobContainerName get;
string StorageName get;
Task<Stream> DownloadStreamAsyns(string fileName);
Task Upload(string fileId, Stream stream);
public class NamedServiceProvider<T>
where T : INamedService
readonly IReadOnlyDictionary<string, T> Instances;
public NamedServiceProvider(
IEnumerable<T> instances)
Instances = instances?.ToDictionary(x => x.Identifier) ??
throw new ArgumentNullException(nameof(instances));
public bool TryGetInstance(string identifier, out T instance)
return Instances.TryGetValue(identifier, out instance);
public class AzureBlobStorage : IAzureBlobStorage
public string Identifier get;
private BlobContainerClient _client;
private string _containerName;
private string _storageName;
public string StorageName => _storageName;
public string AzureBlobContainerName => _containerName;
public AzureBlobStorage(string identifier, BlobContainerClient client, string containerName, string storageName)
Identifier = identifier;
_client = client;
_containerName = containerName;
_storageName = storageName;
public async Task<Stream> DownloadStreamAsyns(string fileName)
return await _client.GetBlobClient(fileName).OpenReadAsync();
public async Task Upload(string fileId, Stream stream)
await _client.GetBlobClient(fileId).UploadAsync(stream);
然后是静态扩展方法:
public static IServiceCollection AddAzureStorage(
this IServiceCollection services,
IConfiguration configuration,
string identifier)
var options = new ABlobStorageConfigurationOptionsDTO();
configuration
.GetSection("AzureBlobStorageConfiguration")
.GetSection("Storages")
.GetSection(identifier)
.Bind(options);
return services
.TryAddTransient<NamedServiceProvider<IAzureBlobStorage>>()
.AddTransient<IAzureBlobStorage, AzureBlobStorage>(isp =>
var client = new BlobServiceClient(options.ConnString);
var container = client.GetBlobContainerClient(options.AzureBlobContainerName);
var containerName = options.AzureBlobContainerName;
var storageName = options.StorageName;
return new AzureBlobStorage(identifier, container, containerName, storageName);
);
然后你可以这样调用它:
public Controller(NamedServiceProvider<IAzureBlobStorage> azureStorage)
_ = azureStorage ?? throw new ArgumentNullException(nameof(azureStorage));
_azureStorage2 = azureStorage.TryGetInstance("Storage2", out var instance) ? instance : throw new Exception("Something about the identifier not being found??");
我在智能感知环境之外对此进行了编码,如果有任何较小的拼写错误或错误,敬请见谅。可能有更好的方法来做到这一点,但这似乎至少有点ok-ish?哦,我只改变了我必须做的事情,以使其能够正常工作。我不想触及任何其他逻辑..
【讨论】:
以上是关于同一存储的依赖注入多个接口(具有不同的connectionString)的主要内容,如果未能解决你的问题,请参考以下文章