2012-07-31 13 views
10

Ho un'architettura abbastanza ben progettata in cui i controller vanno ai servizi che accedono ai repository che comunicano con il database.Come mantenere i controller piccoli in ASP.NET MVC?

Come tale, la logica nei controllori è mantenuta ad un minimo, ma ancora molto sottili pezzi di codice che eseguono alcuni compiti come

  • convalidare il modello
  • organizzare gli argomenti del metodo azione
  • invocare alcuni servizi con questi argomenti, magari convalidare il risultato e restituire la vista se il modello è ora non valido
  • infine produrre un modello dal risultato del servizio e restituirlo.

alcuni casi più lunghi fanno cose diverse a seconda di uno "stato" restituito dal servizio.

Ecco un paio di esempi:

[HttpPost] 
[AjaxOnly] 
[Authorize] 
public JsonResult Preview(string input) 
{ 
    LinkResult parsed = linkService.ParseUserInput(input); 
    if (parsed.Result == LinkParseResult.Used) 
    { 
     long? postId = parsed.Link.PostId; 
     if (postId.HasValue) 
     { 
      Post post = postService.GetById(postId.Value, false); 
      return Json(new 
      { 
       faulted = "used", 
       link = DetailsRoute(post), 
       id = postId 
      }); 
     } 
     else 
     { 
      return Json(new { faulted = "invalid" }); 
     } 
    } 
    else if (parsed.Result == LinkParseResult.Invalid) 
    { 
     return Json(new { faulted = "invalid" }); 
    } 
    else 
    { 
     Link link = parsed.Link; 
     if (link.Description != null && link.Description.Length > 200) 
     { 
      link.Description = link.Description.Substring(0, 200); 
     } 
     return AjaxView(link); 
    } 
} 

e (Post viene dal dominio, PostModel è il modello vista)

private PostModel PostModelConverter(Post post) 
{ 
    Link link = post.Link; 
    if (link == null) 
    { 
     throw new ArgumentException("post.Link can't be null"); 
    } 
    if (link.Type == LinkType.Html) 
    { 
     return new PostedLinkModel 
     { 
      Description = link.Description, 
      PictureUrl = link.Picture, 
      PostId = post.Id, 
      PostSlug = postService.GetTitleSlug(post), 
      Timestamp = post.Created, 
      Title = link.Title, 
      UserMessage = post.UserMessage, 
      UserDisplayName = post.User.DisplayName 
     }; 
    } 
    else if (link.Type == LinkType.Image) 
    { 
     return new PostedImageModel 
     { 
      PictureUrl = link.Picture, 
      PostId = post.Id, 
      PostSlug = postService.GetTitleSlug(post), 
      Timestamp = post.Created, 
      UserMessage = post.UserMessage, 
      UserDisplayName = post.User.DisplayName 
     }; 
    } 
    return null; 
} 

Ciò solleva la questione su se visualizzare i modelli in realtà dovrebbe essere in di norma, o potrebbero essere parte del dominio o qualche altro progetto.

Non sono sicuro di poter fare molto sull'azione di anteprima, a parte forse usare un PreviewModel che riceve il collegamento e tronca la descrizione, ma questo salverebbe come, due righe.

Il convertitore di modello dovrebbe essere probabilmente da qualche altra parte, ma non ho idea di dove dovrebbe essere.

Un altro punto che viene in mente è se dovrei suddividere questo controller usando la parola chiave partial (è una cattiva pratica usare questo per qualcosa di diverso dalle classi autogenerate?), O aggiungendo percorsi che usano controller diversi a seconda di quale azione è richiesta o quale metodo http viene utilizzato, qual è il solito modo di gestirlo?

+0

Solo una preferenza personale, ma è possibile utilizzare le istruzioni switch invece di if .. else if ... ecc. –

risposta

4

questo è stato chiesto più volte:
Business logic in the controller
Where should I put my controller business logic in MVC3
Keep Controllers Thin

Così come scritto su altrove:
ASP MVC Best Practices - Skinny Controllers
Keep Controllers Thin

La comunità sembra avere un buon consenso sul fatto che questo tipo di logica appartiene al di fuori dei controllori. Generalmente nel modello (o ViewModel), ma da qualche parte nel livello aziendale.

Come nota finale, l'utilizzo di partials per codice non generato automaticamente non è sconsigliato. Se ha senso dividere le cose, fallo.Pensa a quali sono le tue ragioni per dividerlo. Questo sarà un tipo di cosa, caso per caso.

2
private PostModel PostModelConverter(Post post) 
{ 
    Link link = post.Link; 
    if (link == null) 
    { 
     throw new ArgumentException("post.Link can't be null"); 
    } 
    if (link.Type == LinkType.Html) 
    { 
     var model = AutoMapper.Map<PostedLinkModel>(post); 
     model.PostSlug = postService.GetTitleSlug(post); 
     return model; 
    } 
    else if (link.Type == LinkType.Image) 
    { 
     var model = AutoMapper.Map<PostedImageModel>(post); 
     model.PostSlug = postService.GetTitleSlug(post); 
     return model; 
    } 
    return null; 
} 

http://www.viddler.com/v/b568679c

0

controller non conterrà alcuna logica di dominio

controller dovrebbe essere responsabile solo per:

Convalida dell'input

Calling modello per preparare la vista

Ritorna la vista o reindirizzare a un'altra azione

Se stai facendo qualsiasi altra cosa lo stai facendo in un posto sbagliato, è piuttosto la responsabilità del Modello che stai facendo in Controller.

Se segui questa regola il tuo metodo di azione non sarà più di 20-25 linee di codice. Ian Cooper ha un eccellente post Skinny Controller Fat Model, leggilo.