注册多个具有相同接口但构造函数参数值不同的单例

roc*_*tar 4 c# dependency-injection azure-servicebus-topics .net-core-3.1

我陷入困境,需要一些建议或解决方案。

使用 ASP.NET Core 3.1 的 Web API

启动.cs

services.AddSingleton<ITopicClient>(s => new TopicClient({connectionstring},{topic}));
Run Code Online (Sandbox Code Playgroud)

TopicRepository.cs

 public class TopicRepository : ITopicRepository
 {
        private readonly ITopicClient _topicClient1;
       private readonly ITopicClient _topicClient2;

        public TopicRepository (ITopicClient topicClient1, ITopicClient topicClient2)
        {         
            _topicClient1 = topicClient1;
            _topicClient2 = topicClient2;
        }
        public async Task<Response> SendToTopicAsync(string message, string topic)
        {
           if( topic == "topic1")
           await _topicClient1.send(message);
           else if (topic == "topic2")
           await _topicClient2.send(message);
        }
}
Run Code Online (Sandbox Code Playgroud)

共享库中的 TopicClient.cs

        public TopicClient(string serviceBusConnectionString, string topicName)
        {
           _topicClient = new TopicClient(_serviceBusConnectionString,topicName);
        }
Run Code Online (Sandbox Code Playgroud)

我需要向不同的主题发送消息。我想在startup.cs中注册具有不同主题名称的服务。我想重用 topicClient 连接。

services.AddSingleton(s => new TopicClient({connectionstring},{ topic1 }));

services.AddSingleton(s => new TopicClient({connectionstring},{ topic2 }));

如何通过使用相同接口注册相同类型的单例实例来实现此目的?

先感谢您!

Ron*_*hag 6

您可以使用客户端解析器来保存已注册的客户端,并在客户端周围进行包装。首先,使用名称或枚举围绕您的客户端创建一个包装器,以了解如何解决它。由于我不喜欢魔术字符串,因此我决定在示例中使用枚举。

// Wrapper for your TopicClients
public interface ICustomTopicClient
{
    public ITopicClient TopicClient { get; }
    public TopicName TopicName { get; }
}

// Implement the ICustomTopicClient interface
public class CustomTopicClient : ICustomTopicClient
{
    public ITopicClient TopicClient { get; }
    public TopicName TopicName { get; }
    
    public CustomTopicClient(ITopicClient topicClient, TopicName topicName)
    {
        TopicClient = topicClient;
        TopicName = topicName;
    }
}

// Enum for how to resolve the requested TopicClient
public enum TopicName
{
    Topic1 = 0,
    Topic2 = 1
}

// Register all ICustomTopicClients in your container
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic}), TopicName.Topic1));
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic2}), TopicName.Topic2));
Run Code Online (Sandbox Code Playgroud)

然后,您创建一个包含所有自定义客户端的解析器。您从容器注入客户端集合,并使用公共方法创建一个字典来解析客户端。

public interface IMessageClientResolver
{
    ITopicClient ResolveClient(TopicName name);
}

public class MessageClientResolver : IMessageClientResolver
{
    private readonly Dictionary<TopicName, ITopicClient> topicClients;

    public MessageClientResolver(IEnumerable<ICustomTopicClient> clients)
    {
         topicClients = clients.ToDictionary(k => k.TopicName, v => v.TopicClient);
    }

    public ITopicClient ResolveClient(TopicName name)
    {
        topicClients.TryGetValue(name, out var client);
        if (client is null)
            throw new ArgumentException(nameof(client));

        return client;
    }
}
Run Code Online (Sandbox Code Playgroud)

将解析器注册到容器。

services.AddSingleton<IMessageClientResolver, MessageClientResolver>();
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

public class Foo
{
    private readonly ITopicClient topicClient;
    private readonly ITopicClient topicClient2;

    public Foo(IMessageClientResolver clientResolver)
    {
        topicClient = clientResolver.ResolveClient(TopicName.Topic1);
        topicClient2 = clientResolver.ResolveClient(TopicName.Topic2);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用相同的模式并使用 IQueueClients 扩展解析器。并添加一个解析方法以通过 QueueName 枚举返回 IQueueClient。