Gestione dinamica delle entità con Entity Framework 6

Quando un ORM è quello che ti serve Entity Framework può aiutarti anche con i requisiti più complessi

Quando abbiamo fondato Blexin l'idea era quella di creare un CMS (Content Management System) basato sulla tecnologia Asp.Net. Ispirandoci al CMS Orchard di Microsoft (oggi è un progetto open-source non dipendente da Microsoft), volevamo realizzare un prodotto che soddisfacesse tre requisiti principali:

  • Creare un “prodotto” per realizzare siti web in maniera semplice e veloce
  • Creare una "piattaforma" per web agency, alternativa a Wordpress e Joomla, per gestire i propri progetti
  • Creare un “framework” per sviluppatori Asp.Net che permettesse di aggiungere feature di un CMS ai propri progetti

Dopo tre anni di sviluppo e tre major release di WebRight (è questo il nome del nostro CMS) è arrivato il momento di prendere il bello e il brutto di questa lunga esperienza e rifattorizzare il codice per la quarta versione del nostro principale progetto, che sarà anche la prima versione pubblica!

Facendo refactoring del codice ho riesaminato una delle feature introdotte nella terza versione di WebRight, la gestione dei plug-in. E' una feature semplice da implementare usando la reflection del .Net Framework, ma comunque introduce un problema da risolvere: bisogna aggiungere dinamicamente (dopo il caricamento del plug-in) le entità del plug-in al DbContext di Entity Framework, applicando le corrette (e tipicamente custom) configurazioni e creare le corrispondenti tabelle nel database.

Fortunatamente usiamo Entity Framework Code-first fin dalle prime versioni, quindi possiamo usare l'override del metodo OnModelCreating della classe base DbContext per intercettare la pipeline della creazione del modello e inserire i nostri comportamenti personalizzati.

Per aggiungere una entità al DbContext possiamo usare il metodo “Entity” della classe “DbModelBuilder”. Usando la reflection possiamo ottenere un riferimento a questo metodo scrivendo:

Per aggiungere una specifica entity configuration è disponibile il metodo “Add” della classe “ConfigurationRegistrar”. Ci sono alcuni overload di questo method, a noi serve quello che ha come parametro EntityTypeConfiguration:

Ok, adesso dobbiamo recuperare tutti i plug-in caricati ed estrarre le sole classi che estendono la classe WebRightBaseEntity (ma è possibile usare qualsiasi elemento per marcare la classe che rappresenta il tuo data model):

Usando la reflection di .Net diventa tutto molto semplice. La proprietà this.webRightPluginsManager.Plugins ci restituisce tutti gli assembly .Net caricati come plug-in, è un semplice manager che analizzeremo più approfonditamente nei prossimi post .

Come probabilmente saprete è possibile personalizzare come Entity Framework mappa una entità con una tabella, creando una classe che estende EntityTypeConfiguration (con la entity desiderata come parametro del generic) e usare la fluent API per configurare le proprie preferenze di mapping. Quindi se il plug-in contiene delle configurazioni che ci servono, vanno recuperate. Per ogni entità è possibile avere una sola configurazione, quindi per la entity corrente dobbiamo cercare, mediante reflection, tale configurazione:

A questo punto abbiamo una entity e, se presente, la sua configurazione. Cosa ci facciamo con questi oggetti? Se la configurazione è presente ci serve una sua istanza e una istanza del configurationMethod per la entity corrente:

Con questi oggetti possiamo invocare il metodo per la configurazione passandogli l'istanza come parametro:

Se la configurazione non è presente dobbiamo invocare il nostro entityMethod senza parametri:

L'oggetto modelBuilder usato in entrambi i casi è un parametro del metodo OnModelCreating.

Abilitando la migration di Entity Framework sul nostro progetto, e impostando l'aggiornamento del database quando il modello cambia, possiamo aggiungere un plug-in al progetto e lasciare a Entity Framework il compito di creare le corrispondenti tabelle per noi.

Bello, vero? Happy coding