Disclaimer : il seguente è il mio approccio e non le sacre scritture. Sono al lavoro, quindi ho scritto questo rapidamente e alcuni codici potrebbero non funzionare / compilare!
Controller
Sono componenti del livello di presentazione in modo che non siano interessati alla logica del business o del database. La logica di convalida è un sottoinsieme della logica aziendale.
servizi
Implemento il pattern Service Layer che è fondamentalmente un insieme di classi che sono liberamente associate ai tuoi modelli di dominio e che agiscono su di esse. Queste classi di servizio fanno tutta la logica aziendale. Questo include il materiale del database. Se non sei troppo entusiasta di mettere la logica del database qui puoi implementare il modello di repository e iniettare le classi di repository nelle tue classi di servizio.
Quindi ora il flusso delle cose sta diventando chiaro
Controller -> Service -> Repository
e questo equivale a
ViewModel to Model mapping -> Validation and other business logic -> CRUD operations
Ogni classe di servizio è descritta da un'interfaccia, quindi ora puoi usare DI per iniettarle nei tuoi Controller in questo modo:
readonly IClientService _clientService;
public ClientsController(IClientService clientService)
{
_clientService = clientService;
}
Ora è chiaro esattamente quali dipendenze ha il tuo Controller dato che è tutto lì nel costruttore. I controller sono anche più puliti dal momento che tutta la logica aziendale è nel livello di servizio. Ecco un esempio di inserimento:
[HttpPost]
public ActionResult Insert(ClientViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
var client = new Client();
viewModel.MapTo(client);
_clientService.Insert(client);
return View(viewModel);
}
Ora la parte di convalida
Il tipo di convalida che costituisce il controllo del database è ciò che chiamo convalida profonda. Metto una convalida semplice (superficiale) nel ViewModel (come te) e una profonda validazione nei servizi di livello aziendale aka.
Quindi la convalida profonda che deve essere eseguita quando si inserisce un Client
si trova nel metodo Insert
di ClientService
. Qui vengono eseguiti tutti i controlli del database, forse chiamando un repository o direttamente nel servizio se non stai utilizzando i repository.
Quindi se client
non è valido, come possiamo restituire gli errori di convalida al Controller?
Facciamo un'eccezione poiché è a questo che si applicano le eccezioni. Ma sarà la nostra eccezione personalizzata:
public class MyException : Exception
{
public MyException()
{
}
public MyException(string message)
: base(message)
{
}
public MyException(string message, Exception inner)
: base(message, inner)
{
}
}
Quindi quando le cose non sono valide (nel nostro ClientService
):
public void Insert(Client client)
{
if (client == null)
{
throw new MyException("This thing's not right..");
}
Ci siamo assicurati che l'inserimento fallisse nel più breve tempo possibile, il che è positivo.
Ora tutto ciò che rimane è catturare queste eccezioni personalizzate nel nostro controller. Puoi farlo in molti modi. Uno è usare le clausole try/catch
:
try
{
_clientService.Insert(client);
}
catch (MyException e)
{
// Add error to ModelState perhaps
}
ma copriranno completamente il controller.
L'approccio MVC corretto consiste nel catturare tutti gli errori nel metodo OnException
del tuo controller:
protected override void OnException(ExceptionContext exceptionContext)
{
exceptionContext.ExceptionHandled = true;
if (exceptionContext.Exception is MyException)
{
// Perhaps add to ModelState
}
else
{
// Add a generic error message to ModelState and log error
}
filterContext.Result = new ViewResult
{
ViewName = ...
};
}
Puoi inserire questo codice in un BaseController
da cui erediteranno tutti gli altri controller per evitare la duplicazione del codice.
Questa è la struttura di base in cui lavoro. Spero di non aver reso le cose troppo complicate o confuse.