ASP.NET MVC Action Filter Example: Ensuring an Entity Exists

Categories: ASP.NET MVC

Many times in ASP.NET MVC action methods, you are passing in a key (such as an id parameter) that you will use to lookup an entity in your database with.  Typically this lookup is done with an ORM such as NHibernate or Entity Framework or it could be RavenDB or something else.  The point is, you spend a lot of time taking the key parameter and looking it up in your database and checking to see if the database returned the correct object.

Since this is such a common task, we should make an ActionFilter that can do the check for us and ensure that the entity really exists in the database.  This example uses RavenDB to access the database, but it could be replaced with any code that you use for your particular implementation.  It uses a domain existing of an Order type that represents a customer’s order at a store:

public class OrderExistsAttribute : ActionFilterAttribute
    {
        protected string parameterName = "id";
        protected Order order;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            if (!filterContext.ActionParameters.ContainsKey(ParameterName))
            {
                throw new InvalidOperationException("Missing parameter: " + ParameterName);
            }

            string id = filterContext.ActionParameters[ParameterName] as string;
            if (id == null)
            {
                throw new ArgumentNullException("id is required.");
            }

            order = RavenDb.CurrentSession.Load<Order>(id);
            if (order == null)
            {
                throw new Exception("Specified Order does not exist.");
            }
        }

        /// <summary>
        /// Gets/sets the parameter name that is used to identify the parameter that will act 
        /// as an order key.  Default is 'id'.
        /// </summary>
        public string ParameterName
        {
            get { return parameterName; }
            set { parameterName = value; }
        }
    }

The RavenDb class is just a helper that I use that puts the current request’s IDocumentSession in a static accessor called CurrentSession (this in turn reads it out of the HttpContext.Items collection to get a thread-local session for that particular request). 

You can set the ParameterName property to the name of the parameter in your action method.  Typically this is just ‘id’ so that is the default that is used. 

Here’s how to use this attribute on an action method:

[OrderExists]
public ActionResult Index(string id)
{
  // you are guaranteed at this point that the
  // order specified by id actually exists

}

If you were using NHibernate or RavenDB for this (as I was), you can call the Load() method of the database session to retrieve the Order instance by that id and since it was already loaded by the action filter, it will be instantly returned by the session cache.

You could also derive from this class to create new scenarios such as not only checking for the existence of the Order but also that it is in a particular status/state.  For example, if we have a Save() action method, we probably don’t want to allow saving an order that has already completed.  Once the Order is loaded, you can do any logic you wish on it.  Security or access to the Order could be enforced in this attribute too.

The bottom line here is that the attribute can be used on any action method and saves us a lot of effort by not having to repeat ourselves.

No Comments