C#.如何在对象内注入依赖项的多个实例
本文关键字:实例 依赖 注入 对象 | 更新日期: 2023-09-27 18:28:38
我有以下类(部分):
class SearchViewModel : BaseViewModel<SearchResultItem>
{
private readonly IDownloader _downloader;
public SearchViewModel( IDownloader downloader)
: base(model)
{
_downloader = downloader;
}
private void Download(object sender, DoWorkEventArgs e)
{
_downloader.Download(item);
}
}
我为IDownloader使用了构造函数注入,在多线程进入我的生活之前,它工作得很好。
_downloader有一个状态,我需要在单独的线程中运行_downloader.Download(项)(用户单击搜索结果页面上的下载按钮)。
目标:在_downloader.Download(item)
之前,应该初始化_downloader
的新实例。我可以使用_container.Resolve(IDownloader)
,但它会破坏CompositionRoot原则。
我提出这个问题是为了讨论最佳解决方案,因为我认为直接初始化(new())或引用容器不是答案。
为什么不直接对工厂进行手工操作?这是一种非常常见的依赖注入代码模式。
interface IDownloaderFactory
{
IDownloader Create();
}
class DownloaderFactory : IDownloaderFactory
{
IDownloader Create()
{
// either new it up here, or resolve from the container as you wish.
}
}
然后将该工厂注入到原始对象中。
class SearchViewModel : BaseViewModel<SearchResultItem>
{
private readonly IDownloaderFactory _factory;
public SearchViewModel( IDownloaderFactory factory)
: base(model)
{
_factory = factory;
}
private void Download(object sender, DoWorkEventArgs e)
{
_factory.Create().Download(item);
}
}
这样,您就不依赖于特定于IOC容器的功能。
实现显然取决于您使用的容器,但如果我需要在Autofac中做类似的事情,我可能会这样做:
public SearchViewModel(Func<Owned<IDownloader>> downloaderFactory)
: base(model)
{
_downloaderFactory = downloaderFactory;
}
private void Download(object sender, DoWorkEventArgs e)
{
_downloaderFactory().Value.Download(item);
}
Owned<T>
是一个Autofac类,它表示一个不属于容器的T实例-解析它的类负责处理它。我不能100%确定Autofac是否会在每次调用Func<IDownloader>
时向我返回一个新的IDownloader
实例,所以我会使用Owned<T>
来确定。
有些IoC容器没有所有权/生存期跟踪的概念,因此对Func<IDownloader>
进行依赖就足够了——每次都可以保证获得一个新实例。
我不确定我是否完全理解这个问题,但如果你只想每个线程有一个新实例,你通常可以绑定指定的实例。例如,在Ninject中,您可以指定
.InThreadScope()
在绑定结束时。
更新
您没有提供如何进行基础绑定的详细信息。但在Ninject中,让我们假设您每次都想将IDownloader绑定到MyDownloader,但您希望每个线程都有相同的MyDownloader实例,您可以使用
Bind<IDownloader>.To<MyDownloader>().InThreadScope();
类似于Matt Hamilton的解决方案,但适用于Microsoft的DI容器。
定义Func<IDownloader>
构造函数参数:
public SearchViewModel(Func<IDownloader> resolveDownloader)
: base(model)
{
_resolveDownloader = resolveDownloader;
}
要知道,MicrosoftDI容器不知道该为这样的参数传递什么。所以你需要像这样注册SearchViewModel
:
serviceCollection.AddTransient<SearchViewModel>(s =>
new SearchViewModel(() => s.GetRequiredService<IDownloader>()));