Asp.net mvc web api validazione del modello best practice

2

Ho un fastidioso odore di codice nella mia API di asp.net che sto passando in giro, e non riesco a trovare un modo per risolvere il problema.

In un'azione del controller MVC, di solito c'è una logica molto diretta, almeno nel mio caso.

  • Convalidare il modello (prima di chiamare l'azione)
  • dati rilevanti sulle query
  • Esegui la logica aziendale
  • Invia risposta

Ora in alcune azioni, la logica deve essere un po 'più complessa.

A volte, le convalide del modello richiedono una query al database.

L'utilizzo di FluentValidation di solito è sufficiente solo per iniettare la dipendenza richiesta e usarlo per interrogare il database - e poi avere il controller interrogare altri dati rilevanti.

Ora è qui che tutto viene incasinato, ho molte convalide del modello che richiedono gli stessi dati dell'azione del controller.

quindi, quello che faccio al momento, in questi casi mi sembra un codice olfattivo.

  • Convalidare il modello, interrogando solo i dati che non sono richiesti dal controllore
  • Interrogare i dati rilevanti e convalidare nel controller
  • Esegui la logica aziendale
  • Invia risposta.

Non mi piace l'idea di convalidare i dati all'interno del controller, poiché spesso si traduce in un'azione di 60 righe, la maggior parte delle quali è la convalida del modello e interrompe SOLID che potrebbe generare confusione man mano che l'app cresce.

L'opzione di interrogare i dati due volte sembra un'opzione estremamente negativa e sinceramente non vedo alcun motivo per sceglierli effettivamente.

Quindi la domanda è fondamentalmente, come suggerisci di convalidare BindingModels sul database?

Il metodo ideale per me sembra in qualche modo iniettare i dati interrogati all'implementazione di AbstractValidator (o qualsiasi altra classe responsabile della convalida), e al controller, e in questo modo riesco a mantenere la logica originale, senza interrogare gli stessi dati due volte. Tuttavia, sono abbastanza sicuro che l'implementazione è peggio.

    
posta gilmishal 19.07.2017 - 18:01
fonte

2 risposte

1

Vedo due approcci che possono essere utilizzati nel tuo caso

1. È possibile modificare il metodo di convalida per restituire i dati necessari per ulteriori azioni. Quindi il metodo di convalida avrà un aspetto simile a

public ValidationResult Validate(DataModel model, IDataService dataService)
{
    var requiredData = dataService.Load();
    var isModelValid = // do validation
    return new ValidationResult
    {
        IsValid = isModelValid,
        Model = dataModel,
        Data = requiredData
    };               
}

La responsabilità del metodo del controller rimarrà la stessa: combinare le azioni richieste e restituire i risultati.

public IActionResult DoSomething(Model model)
{
    var result = _validationService.Validate(model, _dataService);

    var otherRelatedData = _dataService.LoadOtherData();
    var data = CombineData(result.Data, otherRelatedData);

    var actionResult = _businessLogic.Execute(model, data);

    return Ok(actionResult);
}

Il codice sopra riportato è una sorta di pseudo codice: sentitevi liberi di refactarlo, spostate le responsabilità che soddisfano il vostro progetto applicativo.

L'idea principale che restituendo i dati già caricati come parte del risultato della convalida ti impedirà di avere uno stato cache "nascosto" nelle istanze del servizio dati. Avere funzioni di input-output rende il codice una piccola offerta facilmente da seguire / capire.

2. Un altro approccio sarà caricare i dati richiesti e passarli al metodo di convalida. Quindi puoi riutilizzare questi dati per le azioni commerciali.

public IActionResult DoSomething(Model model)
{
    var dataForValidation = _dataService.LoadValidationData();
    var result = _validationService.Validate(model, dataForValidation);

    var otherRelatedData = _dataService.LoadOtherData();
    var data = CombineData(dataForValidation, otherRelatedData);

    var actionResult = _businessLogic.Execute(model, data);

    return Ok(actionResult);
}
    
risposta data 21.10.2017 - 00:06
fonte
0

Quello che suggerirei è creare un livello dati.

Ora non esegui query sul database, ma contro il tuo datalayer. Il datalayer sarebbe lo stesso per la durata della richiesta (scegli la tua struttura di iniezione delle dipendenze preferita per restituire lo stesso datalayer sia nella logica di validazione che nel controller).

Una volta che sai quali dati stai riutilizzando, aggiungi semplicemente alcune logiche di memorizzazione nella cache al tuo livello dati. Se il controller richiede gli stessi dati già interrogati, è già disponibile e non è necessaria una seconda query. Se il tuo controller e amp; la validazione in realtà non usa i dati in qualche percorso di codice, non è nemmeno necessario interrogarli (non è necessario sapere quali sono i dati rilevanti in anticipo)

aggiornamento: puoi anche ottenere alcuni vantaggi tra le chiamate. Ad esempio, se leggo questa domanda di stackexchange, è più probabile che io crei una risposta ad essa. Ma i dati per la domanda sono già nella cache dalla lettura, quindi non devo ri-interrogarlo durante la parte di risposta.

    
risposta data 19.07.2017 - 21:28
fonte

Leggi altre domande sui tag