Una delle best practice nello sviluppo di applicazioni con il pattern MVC è denominata "thick ViewModels, thin Controllers" e, in buona sostanza, separa le responsabilità di controller e view model nella gestione della response. Immaginiamo per esempio di voler modificare un Customer; in questo scenario:
- il view model implementa la logica di business, deve istanziare il repository, recuperare il dato esistente, apportare le modifiche e procedere al salvataggio;
- il controller, e in particolare la action, deve invece preoccuparsi del flusso della richiesta, restituendo la view di errore nel caso il salvataggio non vada a buon fine o effettuando il redirect verso l'elenco dei customer se tutto è andato bene.
Si tratta di un approccio un po' differente rispetto a quanto, per esempio, troviamo comunemente nella maggior parte degli esempi e delle demo in rete, inclusi i template di default di ASP.NET MVC, ma è senza dubbio il modo più efficace per gestire il layer di UI quando le applicazioni diventano complesse.
Abbiamo già visto in passato (https://www.aspitalia.com/script/1139/Dependency-Injection-ASP.NET-MVC-Ninject.aspx) come, sfruttando Ninject, possiamo iniettare automaticamente le dipendenze all'interno di un controller. Se però la nostra logica di business risiede in massima parte nei view model, è in questi oggetti che avremo bisogno di servizi e repository, invece che nei controller.
Il nostro view model, quindi, finirà per avere un'implementazione di massima simile alla seguente, in cui nel costruttore ci aspettiamo di ottenere la reference al repository, mentre esponiamo un metodo Save che contiene tutta la logica per il salvataggio:
public class EditCustomerViewModel { private IRepository<Customer> _customers; public EditCustomerViewModel(IRepository<Customer> customers) { _customers = customers; } public void Save() { var customer = _customers.Get(this.Id); // .... altro codice qui .... } }
La action di modifica del customer, invece, si limiterà a invocare il metodo Save e a gestire il risultato da reinviare al browser:
[HttpPost] public ActionResult Edit(int id, EditCustomerViewModel model) { if (this.ModelState.IsValid) { model.Save(); return this.RedirectToAction("Index"); } else { return this.View(model); } }
Se proviamo a mettere in piedi questa soluzione, però, otterremo un errore, perché ASP.NET, e in particolare il DefaultModelBinder, richiede che il view model abbia un costruttore senza parametri.
Per risolvere il problema, è necessario sfruttare Ninject anche per la creazione di questi oggetti. Il package Ninject.MVC5 non ha una soluzione built-in per questo, ma con pochissime righe di codice possiamo costruire un nostro NinjectModelBinder adatto allo scopo:
public class NinjectModelBinder : DefaultModelBinder { private IKernel _kernel; public NinjectModelBinder(IKernel kernel) { _kernel = kernel; } protected override object CreateModel( ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { return _kernel.Get(modelType); } }
Come ultimo passo, non ci resta che registrarlo, per esempio in NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel) { ModelBinders.Binders.DefaultBinder = kernel.Get<NinjectModelBinder>(); }
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Disabilitare automaticamente un workflow di GitHub (parte 2)
Supportare il sorting di dati tabellari in Blazor con QuickGrid
Eseguire una ricerca avanzata per recuperare le issue di GitHub
Modificare i metadati nell'head dell'HTML di una Blazor Web App
Paginare i risultati con QuickGrid in Blazor
Migliorare i tempi di risposta di GPT tramite lo streaming endpoint in ASP.NET Core
Gestione dei nomi con le regole @layer in CSS
Sfruttare al massimo i topic space di Event Grid MQTT
Generare un hash con SHA-3 in .NET
Eseguire i worklow di GitHub su runner potenziati
Aggiungere interattività lato server in Blazor 8
Disabilitare automaticamente un workflow di GitHub