根据查询字符串/派生控制器注入不同的存储库,并根据控制器类型/ ASP.NET MVC注入存储库

时间:2010-06-03 12:22:38

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

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

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,新闻......),而父控制器仍在处理所有搜索,只需一个简单的

return View(results) 

显示特定于推特,新闻或其他

的正确视图

现在这很酷,看起来很棒,我是一个单独的表单,不同的视图显示在同一页面的标签中。这就是这种方法开始变得复杂的地方。表单必须发布到/ Twitter以在Twitter中搜索,/新闻以搜索新闻...这意味着我应该根据我的哪个选项卡更改表单的操作参数,并在表单返回时显示正确的选项卡取决于..网址?疯狂随之而来。

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

现在我认为使用表单中的参数并发布到单个控制器可以减少痛苦。我正在考虑根据此参数注入正确的SearchService。什么是最好的方法?我想过使用模型绑定器,

所以我的ActionMethod看起来像这样:

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

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

ObjectFactory.GetInstance(...);

基于querystring参数来描述要使用的提供程序,这对我来说似乎并不优雅。我感到困惑,帮助:(。

3 个答案:

答案 0 :(得分:6)

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

我不清楚你需要改变哪个运行时值,但假设它是Query,ISearchServiceFactory可能是这样定义的:

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

答案 1 :(得分:2)

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

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

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

即:

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

我需要一种在Abstract工厂中注入容器的方法。 我可能会将容器包装在这样定义的包装器中。那样可以防止我将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);
    }
}

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

答案 2 :(得分:0)

这是一个广泛的评论,而不是解释为什么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根据控制器的类型解析。

我真的想在这里保留StructureMap的好处,而不是一直使用工厂,每个不同的部分。'

请在我的问题结尾处查看我的编辑,以了解我的问题的其他建议。