Dotnet核心依赖注入,如何注册同一类的多个实例



我想知道是否可以使用内置的dotnet核心DI框架来注册同一类的多个实例

例如,我有一个类ToDo,它向客户端发送消息。我想要具有不同注入配置对象的同一ToDo对象的两个实例。因此,在最后,我将拥有ToDo对象的两个独立实例。

这可能吗?


编辑:

假设我有一个图案pub/sub

我有一个名为MessageSubService.cs的泛型类,它的实现看起来像这个

public class ASBMessageSubService : ASBSubService, IASBSubService
{
public ASBMessageSubService(..., IOptions<ASBSubOptions> options): base(options)
}

因此,基于此,我需要创建多个ASBMessageSubService。唯一不同的是传入的IOptionsIOptions是内部访问。如果使用provider.GetRequireServices<T>,则无法访问该属性。

我知道我可以做这个

service.AddSingleton<ASBMessageSubService, IASBSubService>
service.AddSingleton<ASBMessageSubService, IASBSubService>
service.AddSingleton<ASBMessageSubService, IASBSubService>

这将为我注册3个不同的实例。问题是实现是一样的,我将无法通过where `nameof(ASBMessageSubService(;

我也可以注册一个deligate,在那里我可以根据名称或类型来解决它,但这会遇到我上面描述的相同问题,实现的类型将是相同的。

(我知道我可以使用像structuremapautofac这样的库来完成将它们注册为命名实例的任务。但是,我希望在这个项目中避免使用这样的第三方工具。(

对此有什么建议吗?

谢谢!

David G回答了这个问题。我会稍微扩展一下,以帮助您和其他人。

您可以将多个类注册为它们自己,也可以注册为一个接口:

例如

services.AddTransient<Todo>(provider => new Todo(configuration1));
services.AddTransient<Todo>(provider => new Todo(configuration2));
services.AddTransient<Todo>(provider => new Todo(configuration3));
...
services.AddTransient<ITodoWorker, NeedTodos>();

然后对于依赖项注入,采用对IEnumerable<Todo>:的依赖项

public class NeedTodos : ITodoWorker
{
public NeedTodos(IEnumerable<Todo> todos)
{
foreach (var todo in todos)
{
if (todo.Id == "configuration1")
{
// an idea if you need to find a specific Todo instance
}
}
}
}

如果这不仅仅是一个编辑错误,那么这个声明可能是错误的:

service.AddSingleton<ASBMessageSubService, IASBSubService>

最有可能是:

service.AddSingleton<IASBSubService, ASBMessageSubService>

然后,你的类将这些视为多重依赖关系,如下所示:

public class Subscribers
{
public Subscribers(IEnumerable<IASBSubService> subs)
{
...
}
}

至于寻找一个特定的实例,ASBSubOptions中有什么可以区分IASBSubService

让我猜一猜,说它是一个物业Name。订阅者可能有一个名称。在这种情况下:

public interface IASBSubService
{
string Name { get; }
...
}

然后:

public class ASBMessageSubService : IASBSubService, ...
{
public string Name { get; private set; }
public ASBMessageSubService(..., IOptions<ASBSubOptions> options)
{
Name = options.Value.SubscriberName;
...
}
}

现在,如果我们回到我发明的Subscribers类,假设你有这样的东西,那么你可以通过Name:找到一个实例

public class Subscribers
{
public Subscribers(IEnumerable<IASBSubService> subs)
{
var sub = subs.SingleOrDefault(x => x.Name == "Service1");
}
}

如果它是您想要的具体类型,无论是ASBSubService还是ASBMessageSubService,您都可以使用获得具体类型

var sub = subs.SingleOrDefault(x => x.Name.GetType() == typeof(ASBSubService));
// GetType() never returns the interface type (I think) See https://stackoverflow.com/questions/1159906/finding-the-concrete-type-behind-an-interface-instance/1159940

在界面中显示内容以帮助您选择实例类型可能就是您想要的。这是解决这类问题的一种非常常见的方法。

这让我有点紧张。我不得不打开DI机制的引擎盖:

首先,接口和类:

public interface ISubService {} // just a second interface I made up
public interface IASBSubService {}
public class ASBMessageSubService : IASBSubService, ISubService
{
public string Name { get; set; }
public ASBMessageSubService(string name)
{
Name = name;
}
}

现在,我找到了一种将现有注册实例重新注册为不同接口的方法:

services.AddSingleton<ASBMessageSubService>(provider => new SubServiceSubscriber("sub1"));
services.AddSingleton<ASBMessageSubService>(provider => new SubServiceSubscriber("sub2"));
services.AddSingleton<ASBMessageSubService>(provider => new SubServiceSubscriber("sub3"));
// re-register as IASBSubService
foreach (var service in services.Where(s => s.ServiceType == typeof(ASBMessageSubService)).ToList())
{
services.AddSingleton<IASBSubService >(provider => service.ImplementationFactory(provider) as IASBSubService );
}
// re-register as ISubService
foreach (var service in services.Where(s => s.ServiceType == typeof(ASBMessageSubService)).ToList())
{
services.AddSingleton<ISubService>(provider => service.ImplementationFactory(provider) as ISubService);
}

然后依赖于您需要的接口:

public class Subscriptions
{
public Subscriptions(IEnumerable<IASBSubService> subs)
{ ... }
}

public class ServiceManager
{
public ServiceManager(IEnumerable<ISubService> subs)
{ ... }
}

如果有更干净的方法,其他人需要给我看。

最新更新