Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically pass Entity to Controller Action

When adding a controller for a model, the generated actions will look something like this

public ActionResult Edit(int id = 0)
{
    Entity entity = db.Entities.Find(id);
    if (entity == null)
    {
        return HttpNotFound();
    }
    return View(entity);
}

Now in my case I take a string id which can map to DB IDs in several ways, producing several lines of code for retrieval of the correct entity. Copy&pasting that code to every action which takes an id to retrieve an entity feels very inelegant.

Putting the retrieval code in a private function of the controller reduces the amount of duplicate code but I'm still left with this:

var entity = GetEntityById(id);
if (entity == null)
    return HttpNotFound();

Is there a way to perform the lookup in an attribute and pass the entity to the action? Coming from python, this could easily be achieved with a decorator. I managed to do something similar for WCF services by implementing an IOperationBehavior which still does not feel as straight-forward. Since retrieving an entity by id is something you frequently need to do I'd expect there to be a way other than copy&pasting code around.

Ideally it would look something like this:

[EntityLookup(id => db.Entities.Find(id))]
public ActionResult Edit(Entity entity)
{
    return View(entity);
}

where EntityLookup takes an arbitrary function mapping string id to Entity and either returns HttpNotFound or calls the action with the retrieved entity as the parameter.

like image 272
mensi Avatar asked Oct 22 '22 16:10

mensi


1 Answers

You could write a custom ActionFilter:

public class EntityLookupAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // retrieve the id parameter from the RouteData
        var id = filterContext.HttpContext.Request.RequestContext.RouteData.Values["id"] as string;
        if (id == null)
        {
            // There was no id present in the request, no need to execute the action
            filterContext.Result = new HttpNotFoundResult();
        }

        // we've got an id, let's query the database:
        var entity = db.Entities.Find(id);
        if (entity == null)
        {
            // The entity with the specified id was not found in the database
            filterContext.Result = new HttpNotFoundResult();
        }

        // We found the entity => we could associate it to the action parameter

        // let's first get the name of the action parameter whose type matches
        // the entity type we've just found. There should be one and exactly
        // one action argument that matches this query, otherwise you have a 
        // problem with your action signature and we'd better fail quickly here
        string paramName = filterContext
            .ActionDescriptor
            .GetParameters()
            .Single(x => x.ParameterType == entity.GetType())
            .ParameterName;

        // and now let's set its value to the entity
        filterContext.ActionParameters[paramName] = entity;
    }
}

and then:

[EntityLookup]
public ActionResult Edit(Entity entity)
{
    // if we got that far the entity was found
    return View(entity);
}
like image 81
Darin Dimitrov Avatar answered Nov 15 '22 05:11

Darin Dimitrov