Già negli anni '90 Delphi metteva a disposizione le RTTI (Run Time Type Information), un concetto introdotto con C++ per gestire le informazioni sui tipi, anche se erano altamente sconsigliate per problemi di retro compatibilità, dato che le classi cambiavano sempre ad ogni nuova versione del linguaggio. In .NET vi è la Reflection, che in pratica rappresenta la stessa cosa delle RTTI, ideati con un concetto stabile di retro compatibilità, e con una potenza superiore, oserei dire talvolta anche imbarazzante. Infatti si possono raggiungere tali livelli di astrazione sia sulle classi che sulle istanze, che possiamo addirittura arrivare a creare classi a runtime, istanziarle e usarle. In questo articolo parliamo proprio di questo.
Le classi di riferimento
Per esaminare le possibilità offerte dalle classi presenti nella Reflection, faremo riferimento al namespace System.Reflection, mentre per la generazione dinamica avremo bisogno di alcune classi che sono posizionate nel namespace System.Reflection.Emit.
In quest'ultimo namespace possiamo trovare infatti diverse classi Builder, come per esempio:
- MethodBuilder;
- PropertyBuilder;
- FieldBuilder;
- TypeBuilder;
- ... e tante altre ancora.
Come si capirà dal nome, si tratta di classi che "costruiscono" un metodo, una proprietà, un attributo e ovviamente anche un tipo. Nella costruzione dell'oggetto a loro affidato, queste classi consentono la definizione di nomi, visibilità, parametri, ovvero tutto quello che noi normalmente scriveremmo a design-time, può essere fatto (con un pò di pazienza) anche da codice per essere generato a run-time.
Una volta definite le proprietà, i metodi e le classi che vogliamo creare a runtime, è necessario anche compilare il codice, ovvero ottenere il codice IL (Intermediate Language) che ci consente di eseguire il nostro nuovo codice.
Il nostro scenario
Per il nostro esempio ipotizziamo un contesto di lavoro in cui venga utilizzato un modello dati realizzato con LINQ2SQL, dove ci si trovi nella situazione di dover eseguire delle ricerche su questo DataContext, facendosi ritornare i risultati in un "formato" stabilito di volta in volta, in funzione di alcuni fattori.
Utilizzeremo le classi dei namespace visti prima per definire ex-novo una classe, nel nostro caso facendola partire da una già esistente, ovvero filtrando le proprietà di una entity LINQ2SQL per ottenerne un'altra. Nelle potenzialità di queste classi in realtà, è prevista anche la scrittura di codice a runtime, operazione non molto distante da quello che vedremo in questo esempio, ma che comunque non tratteremo qui. Il progetto di esempio sarà una console application e il database sarà AdventureWorks, disponibile qui. Da questo database creiamo (per nostro uso e consumo) un DataContext con solo due tabelle, come vedete nell'immagine che segue:
Il nostro scopo è quello di cercare nei prodotti quelli che contengono nel "Name" una determinata stringa o parte di stringa, e farci tornare una entità costruita ad hoc con campi specifici.
Dato che il nostro scenario è veramente molto semplice, possiamo ipotizzare che tutti gli articoli che rappresentano biciclette appartengano a categorie che abbiano all'interno del loro nome la parola "bike", per cui possiamo distinguere le biciclette dagli altri prodotti semplicemente con questo tipo di ricerca. Una volta fatta questa suddivisione vogliamo creare una classe per le "bike" e una classe per le "non-bike".
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Supportare lo HierarchyID di Sql Server in Entity Framework 8
Ottimizzare le pull con Artifact Cache di Azure Container Registry
Utilizzare i primary constructor di C# per inizializzare le proprietà
Collegare applicazioni server e client con .NET Aspire
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
Utilizzare una qualunque lista per i parametri di tipo params in C#
Utilizzare il metodo ExceptBy per eseguire operazione di sottrazione tra liste
Recuperare automaticamente un utente e aggiungerlo ad un gruppo di Azure DevOps
Conoscere il rendering Server o WebAssembly a runtime in Blazor
Combinare Container Queries e Media Queries
Gestire la cancellazione di una richiesta in streaming da Blazor
Path addizionali per gli asset in ASP.NET Core MVC