Внедрение другого репозитория в зависимости от контроллера запроса/вывода и ввода репозитория в зависимости от типа контроллера /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 здесь, а не использовать заводы для всех разных частей.
Пожалуйста, см. мое редактирование в конце моего вопроса для другого предложения моей проблемы.