【问题标题】:Inject different repository depending on a querystring / derive controller and inject the repository depending on the controller type / ASP.NET MVC根据查询字符串/派生控制器注入不同的存储库,并根据控制器类型/ASP.NET MVC 注入存储库
【发布时间】:2010-06-03 12:22:37
【问题描述】:

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

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

如果您已经构建了类似的东西或知道什么是最好的方法,欢迎提出建议。

现在我认为使用表单中的参数并发布到单个控制器会减轻痛苦。我正在考虑根据此参数注入正确的 SearchService 。最好的方法是什么?我想到了使用模型活页夹,

所以我的 ActionMethod 应该是这样的:

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

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

ObjectFactory.GetInstance(...);

基于描述要使用哪个提供程序的查询字符串参数,这对我来说似乎并不优雅。我觉得卡住了,求助:(。

【问题讨论】:

    标签: asp.net-mvc design-patterns dependency-injection


    【解决方案1】:

    当您需要根据运行时值更改依赖项时,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);
    }
    

    【讨论】:

    • Erf,其实这不太合适。我会有很多工厂,因为我的 SearchService 有更多的依赖关系,这些依赖关系也由结构映射解决。使用这个工厂我会失去所有的好处,我最终会手动完成结构图为我做的所有工作..
    • 查看我的其他建议。你怎么看?
    • 我真的应该这样做吗?那只会删除我的 IOC 的所有使用......我会根据抽象工厂中的参数手动连接我的所有组件?
    • 使用抽象工厂并不排除使用 DI 容器。您可以将所有固定依赖项注入到工厂实现中。请参阅与 DI 相关的抽象工厂的各种用途列表:stackoverflow.com/questions/2280170/…
    • 感谢您的链接,我实施的解决方案完美运行并解决了我的所有问题(请参阅已接受的答案)。我使用抽象工厂模式,但是我将 ioc 容器注入到工厂中。这样,我工厂的行为也是由容器定义的。工厂是 5 行代码。我仍然有一个地方可以更改我的组件的连接方式。我喜欢它:)
    【解决方案2】:

    我试图弄清楚如何使用抽象工厂模式并仍然让结构映射解决我的组件的所有依赖关系。

    我相信这就是我将要实现它的方式,但我在此处提交此内容以获得一些反馈,如果有人会阅读此内容。

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

    即:

    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 泄漏到我的项目中。无论如何,我不需要抽象工厂中的这两个功能,但这不是必需的:

    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】:

      这是一个更广泛的评论,而不是解释为什么 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 的好处,而不是一直使用工厂来处理每个不同的部分。'

      请在我的问题末尾查看我的编辑,以获取针对我的问题的其他建议。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-22
        • 1970-01-01
        • 1970-01-01
        • 2021-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多