Usare Ninject per la creazione dei ViewModel in ASP.NET MVC

di Marco De Sanctis, in ASP.NET MVC,

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

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi