代码之家  ›  专栏  ›  技术社区  ›  Stéphane

根据查询字符串/派生控制器插入不同的存储库,并根据控制器类型/asp.net mvc插入存储库

  •  4
  • Stéphane  · 技术社区  · 16 年前

    我有一个搜索表单,可以在不同的提供者中搜索。 一开始我有一个基本控制器

    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才能在twitter中搜索,发布到/news才能在news中搜索…这意味着我应该根据我所在的选项卡更改表单的操作参数,并在表单返回时显示正确的选项卡,具体取决于..URL?疯狂随之而来。

    如果你已经建立了这样的东西或知道什么是最好的方法,请建议是受欢迎的。

    现在我想使用表单中的一个参数并将其发布到一个控制器上会更省力。我正在考虑根据这个参数注入正确的搜索服务。最好的方法是什么?我想用模型活页夹,

    所以我的actionMethod如下所示:

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

    但我想需要在ModelBinder中进行这样的调用

    ObjectFactory.GetInstance(...);
    

    基于描述要使用哪个提供程序的querystring参数,这对我来说似乎不太优雅。我觉得被卡住了,救命:(。

    3 回复  |  直到 16 年前
        1
  •  6
  •   Sam Holder Brian Adams    16 年前

    每当需要根据运行时值改变依赖关系时, Abstract 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. 
        } 
    } 
    

    我并不完全清楚您需要对哪个运行时值进行更改,但假设是查询,则可以这样定义ISearchServiceFactory:

    public interface ISearchServiceFactory
    {
        ISearchService Create(Query query);
    }
    
        2
  •  1
  •   Sam Holder Brian Adams    16 年前

    我试图弄清楚如何使用抽象工厂模式,仍然让structuremap解析组件的所有依赖项。

    我相信这是我将要实现它的方式,但是我在这里提交这个来获得一些反馈,如果有人会阅读这个。

    正如前面的回答中所解释的,我不想根据抽象工厂中需要的提供者构建整个对象图。

    IE:

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

    我能做的是让抽象工厂使用我的容器根据一个参数(在我的例子中来自于querystring)创建搜索管理器的实例。

    结构映射允许以这种方式创建命名实例:

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

    我需要一种方法在我的抽象工厂中注入容器。 我可能会用这样定义的包装器包装容器。这可以防止我将结构图泄漏到我的项目中。不管怎样,我不需要抽象工厂中的这两个功能,但不需要:

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

    这样看起来很干净:)。 思想?

        3
  •  0
  •   Stéphane    16 年前

    这是一个更广泛的评论,而不是解释为什么抽象工厂看起来很复杂的答案。下面是一些更接近现实的东西:

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

    FacetPository对于任何提供程序都是相同的,但SearchManager更改、ConfigManager更改和BooleanQueryBuilder是一个抽象类,具有不同的提供程序的不同实现(因为每个API的查询不使用相同的关键字),所有这些依赖项当前都由结构映射基于控制器的类型。

    我真的很想在这里保留结构图的好处,而不是一路使用工厂,为每一个不同的部分。”

    关于我的问题的另一个建议,请参阅我问题末尾的编辑部分。

    推荐文章