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
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Utilizzare l'operatore GroupBy come ultima istruzione di una query LINQ in Entity Framework
Eseguire query verso tipi non mappati in Entity Framework Core
Aggiungere interattività lato server in Blazor 8
Effettuare il binding di date in Blazor
Eseguire un metodo asincrono dopo il set di una proprietà in Blazor 8
Utilizzare Tailwind CSS all'interno di React: primi componenti
Copiare automaticamente le secret tra più repository di GitHub
Supportare il sorting di dati tabellari in Blazor con QuickGrid
Implementare l'infinite scroll con QuickGrid in Blazor Server
Usare il versioning con i controller di ASP.NET Core Web API
Personalizzare l'errore del rate limiting middleware in ASP.NET Core
I più letti di oggi
- Riordinare le righe di una GridView di ASP.NET con jQuery
- Creazione di un alarm con suono personalizzato con Windows Phone 7.1
- Utilizzare la session affinity con Azure Container Apps
- Blue-green deployment con Azure Web App e DevOps
- Ed infine anche il calendario :)
- Configurare la diagnostica di Azure attraverso Visual Studio
- Recuperare la data di creazione di un tag tramite una pipeline YAML di Azure DevOps
- Microsoft Security Bulletin MS05-002
- .NET Core e dispositivi IoT
- Eseguire attività pianificate con Azure Container Jobs