6

I have a BlobStorageRepository class which is being used to interact with Azure blob storage. It implements an interface IBlobStorageRepository.

I have the need to use two instances of BlobStorageRepository but they are setup with different credentials:

In my Startup.cs:

// Normal storage
var storageCredentials = new StorageCredentials(_appSettings.BlobStorageAccountName, _appSettings.BlobStorageAccountKey);
var cloudBlobStorageAccount = new CloudStorageAccount(storageCredentials, true);
var cloudBlobClient = cloudBlobStorageAccount.CreateCloudBlobClient();
var blobRepository = new BlobStorageRepository(cloudBlobClient);

// Quarantine storage
var quarantineStorageCredentials = new StorageCredentials(_appSettings.QuarantineBlobStorageAccountName, _appSettings.QuarantineBlobStorageAccountKey);
var quarantineCloudBlobStorageAccount = new CloudStorageAccount(quarantineStorageCredentials, true);
var quarantineCloudBlobClient = quarantineCloudBlobStorageAccount.CreateCloudBlobClient();
var quarantineBlobRepository = new BlobStorageRepository(quarantineCloudBlobClient);

services.AddSingleton<IBlobStorageRepository>(blobRepository);
services.AddSingleton<IBlobStorageRepository>(quarantineBlobRepository);

How can I configure the above so that I can resolve the dependencies in my class and get the two different instances i.e:

private readonly IBlobStorageRepository _blobStorageRepository;
private readonly IBlobStorageRepository _quarantineBlobStorageRepository;

public DocumentService(IBlobStorageRepository blobStorageRepository, IBlobStorageRepository quarantineBlobStorageRepository)
{
    _blobStorageRepository = blobStorageRepository;
    _quarantineBlobStorageRepository = quarantineBlobStorageRepository;
}

Edit: My question is not a duplicate of How to register multiple implementations of the same interface in Asp.Net Core? because I want to pass the instantiated class when adding the service i.e. services.AddSingleton<IBlobStorageRepository>(blobRepository) and you cannot pass the instantiated class to serviceProvider.GetService<BlobStorageRepository>().

4
  • 1
    Possible duplicate of How to register multiple implementations of the same interface in Asp.Net Core? Commented Jul 10, 2019 at 13:41
  • @PavelAnikhouski this is not a duplicate. The use case is different. Commented Jul 10, 2019 at 13:45
  • @chris Idea is the same. You should register all implementations as self + factory to get instance by name and then inject factory not interfaces: _blobStorageRepository = repoFactory.Get(RepoType.Blob); Commented Jul 10, 2019 at 13:52
  • @mtkachenko how can I get instance by name? A code example would great if possible. Commented Jul 10, 2019 at 13:54

3 Answers 3

8

Out of the box, all you can do is inject all instances of the interface using:

public MyClass(List<IBlobStorageRepository> repos)

Then, you'll need to decide for yourself how to pull the right instance out of the list for each ivar you want to set.

It's generally better in this type of scenario to create a factory class and inject that instead. For example, you could have a BlobStorageRepositoryFactory and provide a way to return a repository instance for the particular scenario you require. That could be a method, property, etc. It's up to you. The point is that the factory coordinates the logic for both managing the repositories and providing an API to retrieve the right one.

Alternatively, you can supplement Microsoft.Extensions.DependencyInjection with a more robust DI container. A list of supported containers can be found at: https://github.com/aspnet/Extensions/tree/master/src/DependencyInjection.

Sign up to request clarification or add additional context in comments.

1 Comment

It's not working in .NET 8. Could it be any change in the functionality? I tried with List<IService> and got an exception. But I changed it to IEnumerable<IService> and it worked like a charm.
5

Unfortunately I can't find "named instances" functionality in embeded ASP.NET Core DI container. In Autofac you can use Named and Keyed Services.

In your case you can do something like this:

public enum RepoTypes { Blob, Quarantine }

...

public interface IRepoResolver
{
    //resolve using method
    IBlobStorageRepository GetRepo(RepoTypes rt);

    //resolve usnig indexer
    IBlobStorageRepository this[RepoTypes rt] { get; }
}

...

public sealed RepoResolver : IRepoResolver
{
    private readonly Dictionary<RepoTypes, IBlobStorageRepository> _repos;

    public RepoResolver()
    {
        _repos[RepoTypes.Blob] = new BlobStorageRepository(...);
        _repos[RepoTypes.Quarantine] = new BlobStorageRepository(...);
    }

    public IBlobStorageRepository GetRepo(RepoTypes rt) => _repos[rt];

    public IBlobStorageRepository this[RepoTypes rt] { get => _repos[rt]; }
}

...

services.AddSingleton<IRepoResolver, RepoResolver>();

...

public class Some
{
    public Some(IRepoResolver rr)
    {
        var blob1 = rr.GetRepo(RepoTypes.Blob);
        var blob2 = rr[RepoTypes.Blob];
    }
}

3 Comments

Thanks a lot for this. Have implemented it slightly different but the principles are the same.
@ChrisB Can you please share your implementation. I'm also stuck to this one.
@sureshrajput You can find his implementation in the last answer here. But read my comments to the answer too to understand disadvantages
2

For future reference - the way I solved this using a combination of mtkachenko and Chris Pratt's answers:

Created a BlobStorageRepositoryFactory:

public enum BlobStorageRepositoryType
{
    Core,
    Quarantine
}

public interface IBlobStorageRepositoryFactory
{
    void Register(BlobStorageRepositoryType type, IBlobStorageRepository blobRepository);
    IBlobStorageRepository Resolve(BlobStorageRepositoryType type);
}

public class BlobStorageRepositoryFactory : IBlobStorageRepositoryFactory
{
    private readonly ConcurrentDictionary<BlobStorageRepositoryType, IBlobStorageRepository> _blobRepositories = new ConcurrentDictionary<BlobStorageRepositoryType, IBlobStorageRepository>();

    public void Register(BlobStorageRepositoryType type, IBlobStorageRepository blobRepository)
    {
        _blobRepositories[type] = blobRepository;
    }

    public IBlobStorageRepository Resolve(BlobStorageRepositoryType type)
    {
        return _blobRepositories[type];
    }
}

Then in my Startup.cs:

var blobStorageRepositoryFactory = new BlobStorageRepositoryFactory();
blobStorageRepositoryFactory.Register(BlobStorageRepositoryType.Core, GetBlobStorageRepository(_appSettings.BlobStorageAccountName, _appSettings.BlobStorageAccountKey));
blobStorageRepositoryFactory.Register(BlobStorageRepositoryType.Quarantine, GetBlobStorageRepository(_appSettings.QuarantineBlobStorageAccountName, _appSettings.QuarantineBlobStorageAccountKey));
services.AddSingleton<IBlobStorageRepositoryFactory>(blobStorageRepositoryFactory);

private static BlobStorageRepository GetBlobStorageRepository(string accountName, string accountKey)
{
    var storageCredentials = new StorageCredentials(accountName, accountKey);
    var cloudBlobStorageAccount = new CloudStorageAccount(storageCredentials, true);
    var cloudBlobClient = cloudBlobStorageAccount.CreateCloudBlobClient();
    var blobRepository = new BlobStorageRepository(cloudBlobClient);
    return blobRepository;
}

And finally in my service:

private readonly IBlobStorageRepository _blobStorageRepository;
private readonly IBlobStorageRepository _quarantineBlobStorageRepository;

public DocumentService(IBlobStorageRepositoryFactory blobStorageRepositoryFactory)
{
    _blobStorageRepository = blobStorageRepositoryFactory.Resolve(BlobStorageRepositoryType.Core);
    _quarantineBlobStorageRepository = blobStorageRepositoryFactory.Resolve(BlobStorageRepositoryType.Quarantine);
}

2 Comments

Having void Register(...) method is a bad idea. You use Dictionary inside BlobStorageRepositoryFactory and BlobStorageRepositoryFactory is a singleton. So it's no threadsafe. In your case it's possible to write and read _blobRepositories concurrently from different threads but Dictionary is not thread-safe. It works OK in my example because I initialize data inside constructor so you can't modify it during application lifetime. You still can use ConcurrentDictionary but why? In my example it's thread-safe and still works OK with Dictionary.
@mtkachenko thank you for this. I will use your example instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.