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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With