我想知道是否可以使用内置的dotnet核心DI框架来注册同一类的多个实例
例如,我有一个类ToDo
,它向客户端发送消息。我想要具有不同注入配置对象的同一ToDo
对象的两个实例。因此,在最后,我将拥有ToDo
对象的两个独立实例。
这可能吗?
编辑:
假设我有一个图案pub/sub
我有一个名为MessageSubService.cs
的泛型类,它的实现看起来像这个
public class ASBMessageSubService : ASBSubService, IASBSubService
{
public ASBMessageSubService(..., IOptions<ASBSubOptions> options): base(options)
}
因此,基于此,我需要创建多个ASBMessageSubService。唯一不同的是传入的IOptions
。IOptions
是内部访问。如果使用provider.GetRequireServices<T>
,则无法访问该属性。
我知道我可以做这个
service.AddSingleton<ASBMessageSubService, IASBSubService>
service.AddSingleton<ASBMessageSubService, IASBSubService>
service.AddSingleton<ASBMessageSubService, IASBSubService>
这将为我注册3个不同的实例。问题是实现是一样的,我将无法通过where `nameof(ASBMessageSubService(;
我也可以注册一个deligate
,在那里我可以根据名称或类型来解决它,但这会遇到我上面描述的相同问题,实现的类型将是相同的。
(我知道我可以使用像structuremap
或autofac
这样的库来完成将它们注册为命名实例的任务。但是,我希望在这个项目中避免使用这样的第三方工具。(
对此有什么建议吗?
谢谢!
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)
{ ... }
}
如果有更干净的方法,其他人需要给我看。