I have the following class (part of it):
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);
}
}
I used constructor injection for IDownloader and it had worked fine before multithreading come to my life.
_downloader has a state and I need to run _downloader.Download(item) in separate threads (User clicks download buttons on search result page).
The goal: before _downloader.Download(item)
, a new instance of _downloader
should be initialized. I can use _container.Resolve(IDownloader)
, but it would corrupt a Composition Root principle.
I have created the question to discuss the best solution, because I think that direct initialization (new()) or reference to a container is not the answer.
Why not just handroll a factory? This is a very common pattern with dependency-injected code.
interface IDownloaderFactory
{
IDownloader Create();
}
class DownloaderFactory : IDownloaderFactory
{
IDownloader Create()
{
// either new it up here, or resolve from the container as you wish.
}
}
Then inject that factory into your original object.
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);
}
}
This way you're not dependent on a feature specific to your IOC container.
The implementation obviously depends on which container you're using, but if I needed to do something like that in Autofac, I would probably do this:
public SearchViewModel(Func<Owned<IDownloader>> downloaderFactory)
: base(model)
{
_downloaderFactory = downloaderFactory;
}
private void Download(object sender, DoWorkEventArgs e)
{
_downloaderFactory().Value.Download(item);
}
Owned<T>
is an Autofac class that represents an instance of T that isn't owned by the container - the class that resolves it is responsible for disposing of it. I'm not 100% sure whether Autofac would return me a new instance of IDownloader
with each call to a Func<IDownloader>
, so I would use Owned<T>
to be certain.
Some IoC containers don't have a concept of ownership/lifetime tracking, so taking a dependency on Func<IDownloader>
would be enough - you'd be guaranteed to get a new instance each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With