Внедрить разные хранилища в зависимости от строки запроса / производного контроллера и внедрить хранилище в зависимости от типа контроллера / ASP.NET MVC - PullRequest
4 голосов
/ 03 июня 2010

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

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 для поиска в твиттере, в / News для поиска в новостях ... что означает, что я должен изменить параметр действия формы в зависимости от того, какая вкладка у меня есть, и отображать правильную вкладку, когда форма возвращается в зависимости от .. URL? следует сумасшествие.

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

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

Итак, мой ActionMethod будет выглядеть так:

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

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

ObjectFactory.GetInstance(...);

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

Ответы [ 3 ]

6 голосов
/ 03 июня 2010

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

Вместо внедрения 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);
}
2 голосов
/ 04 июня 2010

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

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

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

т.е.:

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...
         }
    }

}

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

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

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

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

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 для разрешения зависимостей в моей абстрактной фабрике следующим образом:

x.For<IContainer>.Use(new ContainerImpl(this));
x.For<IFactory>.Use<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);
    }
}

Это кажется довольно чистым таким образом :). Мысли?

0 голосов
/ 03 июня 2010

Это более обширный комментарий, чем ответ, объясняющий, почему 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, а не использовать фабрики для каждого отдельного элемента. '

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...