Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject different repository depending on a querystring / derive controller and inject the repository depending on the controller type / ASP.NET MVC

I have a search form that can search in different provider. I started out by having a base controller

public SearchController : Controller
{

    protected readonly ISearchService _searchService

    public SearchController(ISearchService searchService)
    {
        _searchService= searchService;
    }

    public ActionResult Search(...)
    {
        // Use searchService to query and return a view.
    }

}

And child controllers

TwitterController : SearchController
{
    ...
}

NewsController : SearchController
{
    ...
}

I use StructureMap to insert all my dependencies in the controller. With this setup, I was able to change the SearchService depending on the type of the controller being instanciated.

x.For<ISearchService>().ConditionallyUse(o =>
      {
            o.TheDefault.Is.OfConcreteType<NewsSearchService>();

            o.If(c => c.ParentType == typeof(TwitterController))
             .ThenIt.Is.OfConcreteType<TwitterSearchService>();

             ...

      });

That even allowed me to set different Views for each controller, (just putting the corresponding folder (Twitter, News...) and the Parent controller is still handling all the Search, with a simple

return View(results) 

which is displaying the correct view specific to twitter, news, or other

Now that was cool and looked great, I a single form and the different views are displayed in tabs on the same page. That's where it starts to get complicated with this approach. The form has to post to /Twitter to search in twitter, to /News to search in news... which means I should change the action parameter of the form depending on which tab I am and display the correct tab on when the form returns depending on.. the url? craziness follows.

If you have built something like this already or know what's the best approach to this, please advices are welcome.

Now I think I would have less pain using a parameter in the form and posting to a single controller. I am thinking of injecting the correct SearchService depending on this parameter. What would be the best approach? I thought of using a model binder,

So I would have my ActionMethod that look like this:

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

But I think would need to make a call like this in the ModelBinder

ObjectFactory.GetInstance(...);

Based on the querystring parameter that describe which provider to use, and that doesn't seem more elegant to me. I feel stuck, help :(.

like image 656
Stéphane Avatar asked Feb 27 '23 22:02

Stéphane


1 Answers

Whenever you need to vary a dependency based on a run-time value, Abstract Factory is the general solution.

Instead of injecting ISearchService into your Controllers, inject an 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. 
    } 
} 

It is not entirely clear to me which run-time value you need to vary on, but assuming that it's the Query, ISearchServiceFactory might be defined like this:

public interface ISearchServiceFactory
{
    ISearchService Create(Query query);
}
like image 74
Mark Seemann Avatar answered Apr 08 '23 07:04

Mark Seemann