views:

30

answers:

1

Suppose the following route:

{region}/{storehouse}/{controller}/{action}

These two parameters region and storehouse altogether identify a single entity - a Storehouse. Thus, a bunch of controllers are being called in the context of some storehouse. And I'd like to write actions like this:

public ActionResult SomeAction(Storehouse storehouse, ...)

Here I can read your thoughts: "Write custom model binder, man". I do. However, the question is

How to avoid magic strings within custom model binder?
Here is my current code:

public class StorehouseModelBinder : IModelBinder
{
    readonly IStorehouseRepository repository;
    public StorehouseModelBinder(IStorehouseRepository repository)
    {
        this.repository = repository;
    }

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var region = bindingContext.ValueProvider.GetValue("region").AttemptedValue;
        var storehouse = bindingContext.ValueProvider.GetValue("storehouse").AttemptedValue;
        return repository.GetByKey(region, storehouse);
    }
}

If there was a single key, bindingContext.ModelName could be used...
Probably, there is another way to supply all the actions with a Storehouse object, i.e. declaring it as a property of the controller and populating it in the Controller.Initialize.

A: 

I've ended up with another approach. Model binding is not an appropriate mechanism for my purpose. Action Filter is the way to go!

Names are not the same as in the question, but treat Site as Storehouse.

public class ProvideCurrentSiteFilter: IActionFilter
{
    readonly ISiteContext siteContext;

    public ProvideCurrentSiteFilter(ISiteContext siteContext)
    {
        this.siteContext = siteContext;
    }

    void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
    {
    }

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.ActionParameters["currentSite"] = siteContext.CurrentSite;
    }
}

ISiteContext realization analyzes HttpContext.Current and pulls an object from Site Repository. Using HttpContext.Current is not too elegant, agree. However, everything goes through IoC, so testability doesn't suffer.

There is an action filter attribute named ProvideCurrentSiteAttribute which uses ProvideCurrentSiteFilter. So, my action method looks like that:

[ProvideCurrentSite]
public ActionResult Menu(Site currentSite)
{
}
Anton