7

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.

Ievgen Martynov
  • 7,870
  • 8
  • 36
  • 52
  • 1
    What do you mean by 'multiple instances'? What toolkit/library are you using for DI? If Ninject, why not `kernel.Get()` for example, having set up a binding for `IDownloader`? – Kieren Johnstone Mar 21 '12 at 19:58
  • I think this answer has a fairly good description of the alternatives - http://stackoverflow.com/a/5460840/33969 – Reddog Mar 21 '12 at 20:01
  • Maybe "multiple instances" is not best title. I need a new instance of downloader class before Download() method has been called. – Ievgen Martynov Mar 21 '12 at 20:17

4 Answers4

8

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.

Paul Phillips
  • 6,093
  • 24
  • 34
  • Thanks. I think that the factory is good way to do this, but we does not remove dependency - we move it in another place. – Ievgen Martynov Mar 22 '12 at 09:08
  • 2
    @YevhenMartynov The dependency _must_ exist - there's no other way to instantiate! `new` or using the container are your only options. This is one of those cases when the "single composition root" principle is just causing you pain. I'm suggesting a slight modification of the rule: allow the container to be used at the root and in factories, and that's it. – Paul Phillips Mar 22 '12 at 15:18
4

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.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • 1
    Thanks, I use Unity and I will look for similar features. – Ievgen Martynov Mar 21 '12 at 20:14
  • To pass Func<> is a very nice solution. I have never seen it before. But I think to use container class inside the object would cause a new dependency. One more question: can you share your experience where and how better to register the delegate? Thanks again. – Ievgen Martynov Mar 22 '12 at 09:05
  • 1
    This is a great primer for the various ways you can register and resolve dependencies in Autofac: http://code.google.com/p/autofac/wiki/RelationshipTypes – Matt Hamilton Mar 22 '12 at 10:10
0

Similar to Matt Hamilton's solution but for Microsoft's DI container.

Define a Func<IDownloader> constructor parameter:

public SearchViewModel(Func<IDownloader> resolveDownloader) 
    : base(model) 
{ 
    _resolveDownloader = resolveDownloader; 
} 

Know that Microsoft DI container has no idea what to pass for such parameter. So you need to register SearchViewModel like this:

serviceCollection.AddTransient<SearchViewModel>(s => 
    new SearchViewModel(() => s.GetRequiredService<IDownloader>()));
Hossein Ebrahimi
  • 632
  • 10
  • 20
0

I'm not sure I fully understood the question but if you just want a new instance per thread you can usually bind with that specified. For instance in Ninject you would specify

.InThreadScope()

at the end of your binding.

Update

You didn't provide the details on how you were doing the base binding. But in Ninject let's assume you want to bind IDownloader to MyDownloader every time but you want the same instance of MyDownloader per thread, you would use

Bind<IDownloader>.To<MyDownloader>().InThreadScope();
Tod
  • 8,192
  • 5
  • 52
  • 93