One thing that may not be obvious is that the Controller Class in the ASP.NET MVC Framework is an ActionFilter. In actuality, it is not only an ActionFilter ( IActionFilter), but also a ResultFilter ( IResultFilter ), AuthorizationFilter ( IAuthorizationFilter ), and ExceptionFilter ( IExceptionFilter ).
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter
This seems to throw some developers off a bit as they think of an ActionFilter or any of the other filters as a separate class that is associated with an attribute. It is true that most examples of ActionFilters on the web come in the form of an attribute and separate class for maximum reuse, but in reality this is not the only way to attach filters.
BaseController Layer Supertype for Common Functionality
In particular, if you have a common action filter that will be used across all controllers and actions, you could just create a base controller class ( Layer Supertype ) in your ASP.NET MVC Project that all controller's will derive from and place the ActionFilter Code in that base controller class. Here, for example, I can create a BaseController Class that uses the Logging Application Block to log when all actions are executed in derived controllers:
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controller = filterContext.RouteData.GetRequiredString("controller");
string action = filterContext.RouteData.GetRequiredString("action");
Logger.Write(string.Format("Executing {0}Controller's {1} Action...", controller, action));
}
}
Now when I derive HomeController from BaseController logging will be executed before the Index and About Actions are run on the HomeController.
[HandleError]
public class HomeController : BaseController
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
}
Now you don't need to attach some type of LogFilterAttribute or whatever to each and every Action or each and every Controller. It should be noted that although this seems really slick and the natural evolution of refactoring, you have to be careful with this in terms of maintainability. Once you have that functionality in the base class it makes it difficult to then make exceptions when you don't want that ActionFilter code to run for 1 or more actions.
Filter Registry and Custom ControllerActionInvokers
Along those same lines, for those interested in ActionFilters and how they can be dynamically added to Controllers and their Actions at runtime, you might want to take a peek at the Oxite CodePlex Project. In Oxite, you will find a Filter Registry that is populated at runtime with ActionFilters that are associated with certain controllers and their actions. A Custom ControllerActionInvoker reads the registry and executes the Filters in the Filter Registry as appropriate. It is a pretty cool example of extensibility in the ASP.NET MVC Framework and will give you an appreciation for how things work at the lower levels.
As an FYI, for those in the Tampa, Florida area who are interested in the ASP.NET MVC Framework, make sure you attend the Tampa ASP.NET MVC Developer Group that I started. Lots of great information, examples, and discussion on the MVC Framework!!
Hope this helps,
David Hayden
ASP.NET MVC Book Reviews