2013-02-25 19 views
5

Quando si aggiunge un controller per un modello, le azioni generate sarà simile a questapassano automaticamente Entity per azione controller

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

Ora, nel mio caso prendo un id stringa che può mappare a DB ID in diversi modi, producendo diverse linee di codice per il recupero dell'entità corretta. Copia & incollando quel codice per ogni azione che richiede un ID per recuperare un'entità si sente molto inelegante.

Mettere il codice di recupero in una funzione privata del regolatore riduce la quantità di codice duplicato ma io sono ancora a sinistra con questo:

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

C'è un modo per eseguire la ricerca in un attributo e passaggio l'entità all'azione? Venendo da python, questo potrebbe essere facilmente raggiunto con un decoratore. Sono riuscito a fare qualcosa di simile per i servizi WCF implementando uno IOperationBehavior che ancora non si presenta come semplice. Dal momento che il recupero di un'entità dall'ID è qualcosa che devi fare spesso, mi aspetto che ci sia un modo diverso dal copiare il codice & che incolla.

ideale sarebbe simile a questa:

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

dove EntityLookup prende una funzione di mappatura arbitraria string id-Entity eo restituisce HttpNotFound o chiama l'azione con l'entità recuperate come parametro.

+0

Ha fatto si considera sulle espressioni lambda in LINQ ?, che cosa si può fare è, è possibile avere un livello di accesso ai dati separato, in cui è possibile scrivere le funzioni di recupero dei dati e chiamarli all'interno dei metodi di azione (separazione delle preoccupazioni). :), Se hai intenzione di fare metodi privati ​​per ogni recupero, ci saranno molte linee di codici all'interno dei controller. –

+0

1. È ancora possibile inserire la convalida direttamente in 'GetEntityById'. 2. Probabilmente stai cercando un raccoglitore di modelli personalizzato. –

risposta

3

Si potrebbe scrivere un ActionFilter personalizzato:

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; 
    } 
} 

e poi:

[EntityLookup] 
public ActionResult Edit(Entity entity) 
{ 
    // if we got that far the entity was found 
    return View(entity); 
} 
+0

Mi piace questo approccio :) Tuttavia, Entity Framwork sembra restituire un oggetto 'System.Data.Entity.DynamicProxies. *', Quindi il confronto 'entity.GetType()' non funziona come previsto. – mensi

+0

'entity.GetType(). BaseType' funziona – mensi

0

Se state ripetendo codice simile quindi è possibile utilizzare un metodo di estensione.

public static class ControllerExtensions 
{ 
    public static ActionResult StandardEdit<TEntity>(
     this Controller controller, 
     DbContext db, 
     long id) 
     where TEntity : class 
    { 
     TEntity entity = db.Set<TEntity>().Find(id); 
     if (entity == null) 
     { 
      return controller.HttpNotFound(); 
     } 
     return controller.View(entity); 
    } 
} 

public ActionResult Edit(long id = 0) 
{ 
    return this.StandardEdit<Client>(db, id); 
} 

Se siete veramente ripetendo esattamente lo stesso codice di un certo numero di volte, allora questo dovrebbe essere risolto con l'ereditarietà.

Creare un controller comune che eredita da Controller

public class StandardController : Controller 
{ 
    public ActionResult Edit<TEntity>(long id = 0) 
     where TEntity : class 
    { 
     TEntity entity = db.Set<TEntity>().Find(id); 
     if (entity == null) 
     { 
      return HttpNotFound(); 
     } 
     return View(entity); 
    } 
} 

e modificare i controller modello di ereditare da questo nuovo controller:

public class ClientController : StandardController 
{ 
    public ActionResult Edit(long id = 0) 
    { 
     return base.Edit<Client>(id); 
    } 
} 
Problemi correlati