ASP.NET MVC: dove inserire la logica di immissione dei dati che colpisce il database

2

Facciamo una convalida semplice dell'inserimento dei dati con attributi sul viewmodel. Pensa al formato di un indirizzo e-mail, nome cliente obbligatorio, formato di una data, numero civico ecc.

Ora ci sono anche convalide più complesse che colpiranno il database ei servizi esterni: controlla se l'id è valido (servizio esterno), vedi se la persona (probabilmente) è già presente nel database in base al nome / indirizzo (azione del database), controlla la combinazione indirizzo / codice postale (servizio esterno).

Secondo me questa logica non dovrebbe essere nel controller ma in un componente di convalida / servizio di validazione separato (che ottiene DI'd nel controller).

Vorrei la tua opinione su questo.

    
posta Michel 24.08.2016 - 09:16
fonte

2 risposte

4

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.

    
risposta data 24.08.2016 - 10:30
fonte
-1

Puoi creare una cartella denominata "Repository" sotto il tuo progetto e aggiungere una classe chiamata ad esempio "ValidationRepository" e inserire nel controller.

    
risposta data 24.08.2016 - 09:24
fonte

Leggi altre domande sui tag