views:

302

answers:

3

Let’s say I'm developing a helpdesk application that will be used by multiple departments. Every URL in the application will include a key indicating the specific department. The key will always be the first parameter of every action in the system. For example

http://helpdesk/HR/Members
http://helpdesk/HR/Members/PeterParker
http://helpdesk/HR/Categories
http://helpdesk/Finance/Members
http://helpdesk/Finance/Members/BruceWayne
http://helpdesk/Finance/Categories

The problem is that in each action on each request, I have to take this parameter and then retrieve the Helpdesk Department model from the repository based on that key. From that model I can retrieve the list of members, categories etc., which is different for each Helpdesk Department. This obviously violates DRY.

My question is, how can I create a base controller, which does this for me so that the particular Helpdesk Department specified in the URL is available to all derived controllers, and I can just focus on the actions?

Any help is much appreciated

A: 

Disclaimer: I'm currently running MVC Preview 5, so some of this may be new.

The best-practices way: Just implement a static utility class that provides a method that does the model look-up, taking the RouteData from the action as a parameter. Then, call this method from all actions that require the model.

The kludgy way, for only if every single action in every single controller needs the model, and you really don't want to have an extra method call in your actions: In your Controller-implementing-base-class, override ExecuteCore(), use the RouteData to populate the model, then call the base.ExecuteCore().

Alex Lyman
A: 

You can create a base controller class via normal C# inheritance:

public abstract class BaseController : Controller 
{
}

public class DerivedController : BaseController 
{
}

You can use this base class only for controllers which require a department. You do not have to do anything special to instantiate a derived controller.

Technically, this works fine. There is some risk from a design point of view, however. If, as you say, all of your controllers will require a department, this is fine. If only some of them will require a department, it might still be fine. But if some controllers require a department, and other controllers require some other inherited behavior, and both subsets intersect, then you could find yourself in a multiple inheritance problem. This would suggest that inheritance would not be the best design to solve your stated problem.

Craig Stuntz
+3  A: 

I have a similar scenario in one of my projects, and I'd tend to use a ModelBinder rather than using a separate inheritance hierarchy. You can make a ModelBinder attribute to fetch the entity/entites from the RouteData:

public class HelpdeskDepartmentBinder : CustomModelBinderAttribute, IModelBinder {

 public override IModelBinder GetBinder() {
  return this;
 }

 public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState) {
  //... extract appropriate value from RouteData and fetch corresponding entity from database. 
 }
}

...then you can use it to make the HelpdeskDepartment available to all your actions:

public class MyController : Controller {
 public ActionResult Index([HelpdeskDepartmentBinder] HelpdeskDepartment department) {
  return View();
 }
}
Jeremy Skinner