C #.Как внедрить несколько экземпляров зависимости внутри объекта? - PullRequest
6 голосов
/ 21 марта 2012

У меня есть следующий класс (часть его):

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), но это нарушит принцип Composition Root.

Я создал вопрос для обсуждения лучшего решения , потому что я думаю, что прямая инициализация (new ()) или ссылка на контейнер не является ответом.

Ответы [ 3 ]

6 голосов
/ 22 марта 2012

Почему бы просто не управлять фабрикой?Это очень распространенный шаблон для кода с внедрением зависимостей.

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);
    }
}

Таким образом, вы не зависите от конкретной функциив ваш контейнер МОК.

4 голосов
/ 22 марта 2012

Реализация, очевидно, зависит от того, какой контейнер вы используете, но если бы мне нужно было сделать что-то подобное в 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 возвращать мне новый экземпляр IDownloader при каждом вызове Func<IDownloader>, поэтому я бы использовал Owned<T>, чтобы быть уверенным.

Некоторые контейнеры IoC не имеют концепции отслеживания владения / срока службы, поэтому достаточно взять зависимость от Func<IDownloader> - вам гарантированно будет каждый раз получать новый экземпляр.

0 голосов
/ 22 марта 2012

Я не уверен, что полностью понял вопрос, но если вам просто нужен новый экземпляр для потока, вы обычно можете связать его с указанным. Например, в Ninject вы должны указать

.InThreadScope()

в конце вашего переплета.

Обновление

Вы не предоставили подробную информацию о том, как вы выполняли базовую привязку. Но в Ninject давайте предположим, что вы хотите каждый раз связывать IDownloader с MyDownloader, но вам нужен один и тот же экземпляр MyDownloader для потока, вы должны использовать

Bind<IDownloader>.To<MyDownloader>().InThreadScope();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...