Внедрение другого репозитория в зависимости от контроллера запроса/вывода и ввода репозитория в зависимости от типа контроллера /ASP.NET MVC

У меня есть форма поиска, которая может искать в разных провайдерах. Я начал с базового контроллера

public SearchController : Controller
{

    protected readonly ISearchService _searchService

    public SearchController(ISearchService searchService)
    {
        _searchService= searchService;
    }

    public ActionResult Search(...)
    {
        // Use searchService to query and return a view.
    }

}

И дочерние контроллеры

TwitterController : SearchController
{
    ...
}

NewsController : SearchController
{
    ...
}

Я использую StructureMap для вставки всех моих зависимостей в контроллер. С помощью этой настройки я смог изменить SearchService в зависимости от типа управляемого контроллера.

x.For<ISearchService>().ConditionallyUse(o =>
      {
            o.TheDefault.Is.OfConcreteType<NewsSearchService>();

            o.If(c => c.ParentType == typeof(TwitterController))
             .ThenIt.Is.OfConcreteType<TwitterSearchService>();

             ...

      });

Это даже позволило мне установить разные представления для каждого контроллера (просто поместив соответствующую папку (Twitter, News...), а родительский контроллер все еще обрабатывает весь поиск с помощью простого

return View(results) 

который отображает правильный вид, относящийся к твиттеру, новостям или другим

Теперь это было здорово и выглядело отлично, я один вид и разные виды отображаются в закладках на одной странице. То, что с этим подходом начинает усложняться. Форма должна размещать в /Twitter для поиска в twitter, в /News для поиска в новостях... что означает, что я должен изменить параметр действия формы в зависимости от того, на какой вкладке я нахожусь, и отобразить правильную вкладку, когда форма возвращается в зависимости от.. url? сумасшествие следует.

Если вы уже создали что-то вроде этого или знаете, какой лучший подход к этому, пожалуйста, совет приветствуем.

Теперь я думаю, что у меня будет меньше боли, используя параметр в форме и отправку на один контроллер. Я думаю о том, чтобы ввести правильный SearchService в зависимости от этого параметра. Какой был бы лучший подход? Я думал о том, чтобы использовать модельное связующее,

Итак, у меня бы появился ActionMethod:

public ActionResult Search(ISearchService service, Query query)
{
    var results = service.Find(query);
}

Но я думаю, что нужно было бы сделать такой вызов в ModelBinder

ObjectFactory.GetInstance(...);

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

Ответ 1

Я пытался выяснить, как использовать абстрактный шаблон factory, и пусть структура структуры разрешает все зависимости моих компонентов.

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

Как объясняется в предыдущем ответе, я не хочу строить весь граф объекта в зависимости от того, какой провайдер мне нужен в тесте factory.

т.е.:

class StatServiceFactory : IStatServiceFactory
{
    public IStatService Create(string provider)
    {
        switch(provider)
        {
            case "blog":
                return new  StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
                       //How to resolve the Config, the SearchManager, and BooleanQueryBuilder?   
                       //Add more abstract factories? It starts to get messy in my opinion...
         }
    }

}

Что я могу сделать, так это использовать абстрактный factory мой контейнер для создания экземпляра моих менеджеров поиска в зависимости от параметра (исходящего из запроса в моем случае)

Structuremap позволяет создавать именованные экземпляры таким образом:

x.For<ISearchManager>().Use<AbcSearchManager>().Named("Abc");
x.For<ISearchManager>().Use<DefSearchManager>().Named("Def");

Мне нужен способ вставить контейнер в мой Аннотация factory. Я бы, вероятно, упаковал контейнер в оболочку, определенную следующим образом. Это удержит меня от утечки Structuremap в мой проект. В любом случае мне не нужно больше тех двух функций в абстрактном factory, но это необязательно:

public interface IContainerWrapper
{
    object GetInstance<T>();
    object GetNamedInstance<T>(string key);
}

и реализация:

public class ContainerImpl : IContainerWrapper
{
     private readonly Container _container
     public ContainerImpl(Container container)
     {
          _container = container;
     }

     ...
}

И настройте StructureMap для разрешения зависимостей с моим абстрактным factory следующим образом:

x.For<IContainer>.Use(new ContainerImpl(this));
x.For<IFactory>.Use<Factory>()

Мой factory был бы намного проще и создаст мой экземпляр следующим образом:

public class SearchmanagerFactory
{
    private readonly IContainerWrapper _container;

    public SearchmanagerFactory(IContainerProvider containerProvider)
    {
        _container = containerProvider;
    }

    public ISearchManager Create(string provider)
    {
       //eed to handle the bad input for provider.
        return (ISearchManager)
            _container.Resolve<ISearchManager>(provider);
    }
}

Это выглядит довольно чистым:). Мысли?

Ответ 2

Всякий раз, когда вам нужно изменить зависимость, основанную на значении времени выполнения, Аннотация Factory является общим решением.

Вместо того, чтобы вводить ISearchService в свои контроллеры, введите ISearchServiceFactory:

public SearchController : Controller 
{ 
    private readonly ISearchServiceFactory searchServiceFactory;

    public SearchController(ISearchServiceFactory searchServiceFactory) 
    { 
        if (searchServiceFactory == null)
        {
            throw new ArgumentNullException("searchServiceFactory");
        }

        this.searchServiceFactory = searchServiceFactory; 
    } 

    public ActionResult Search(...) 
    { 
        // Use searchServiceFactory to create an ISearchService based on
        // run-time values, and use it to query and return a view. 
    } 
} 

Мне не совсем понятно, какое значение времени выполнения вам необходимо изменить, но если предположить, что Query, ISearchServiceFactory может быть определен следующим образом:

public interface ISearchServiceFactory
{
    ISearchService Create(Query query);
}

Ответ 3

Это более обширный комментарий, чем ответ, чтобы объяснить, почему AbstractFactory кажется сложным. Вот что похоже на реальность:

class StatServiceFactory : IStatServiceFactory
{
    public IStatService Create(string provider)
    {
        switch(provider)
        {
            case "blog":
                return new  StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
                           //How to resolve the Config, the SearchManager, and BooleanQueryBuilder?   
                           //Add more abstract factories? It starts to get messy in my opinion...
        }
    }
}

FacetRepository одинаковый для любого провайдера, но изменения в SearchManager изменяются, а ConfigManager изменяется, а BooleanQueryBuilder - абстрактный класс с различной реализацией для разных поставщиков (поскольку каждый API не использует одно и то же ключевое слово для своих запросов). Все эти зависимости в настоящее время разрешены структурой структуры, основанной на типе контроллера.

Мне бы очень хотелось сохранить преимущество StructureMap здесь, а не использовать заводы для всех разных частей.

Пожалуйста, см. мое редактирование в конце моего вопроса для другого предложения моей проблемы.