Introduzione ad Entity Framework 4.1 Code-First

di Stefano Mostarda, in Entity Framework,

Sin dalla versione 1.0, Entity Framework ha dimostrato di essere un buon prodotto, che però aveva alcune limitazioni di base, che ne rendevano difficile l'utilizzo come l'incapacità di gestire classi POCO, la non testabilità dell'ObjectContext e la non maturità del designer.
Con l'arrivo della versione 4.0, molte di queste limitazioni sono state eliminate. Tuttavia, seppure il motote è stato notevolmente migliorato, classi e metodi con cui interagiamo con il database per recuperare e persistere gli oggetti sono rimaste pressochè identiche (ObjectContext), oppure sono state modificate per venire incontro ad alcune esigenze (ObjectSet).

Il problema di queste classi è che, seppur introducendo miglioramenti all'usabilità di Entity Framework, devono mantenere la retrocompatibilità con la versione 1.0 e quindi sono rimaste legate a scelte di design errate fatte nella precedente versione.
Dopo un'attenta analisi, il team di Entity Framework ha deciso di rompere la compatibilità con il passato ed introdurre un nuovo set di classi, che permettesse di superare i problemi, offrendo metodi e proprietà più semplici da utilizzare e più semplici da integrare in fase di testing.

Durante questa fase di rifacimento delle API, il team di Entity Framework ha lavorato anche ad un nuovo modo di fare il mapping tra le classi di dominio e le tabelle nel database: è così che è nato Code-First.

Le nuove classi e Code-First fanno parte di questo nuovo set di caratteristiche, che sono racchiuse nella Feature CTP, oggetto di quest'articolo. Ora che abbiamo rivisitato brevemente la storia di Entity Framework, possiamo analizzare le nuove caratteristiche della Feature CTP, cominciando proprio da Code-First.

Code-First con Entity Framework

Nella versione attuale di Entity Framework possiamo mappare le classi di dominio (dette anche entità) verso le tabelle nel database sruttando due modi: Database-First e Model-First.

Nell'approccio Database-First, noi importiamo le tabelle del database nel designer di Visual Studio. Il designer genera automaticamente una classe per ogni tabella e per ogni colonna della tabella viene generata un'omonima proprietà nella classe.
In base alle informazioni nel database, il designer genera anche le relazioni tra le classi (ad esempio, mette una proprietà Dettagli nella classe Ordine ed una proprietà Ordine nella classe dettagli). Questo approccio è molto comodo, in quanto rende operativi in tempi brevissimi. Tuttavia, impone che il database sia sviluppato prima del modello; questo non è sempre, però, il percorso che seguiamo per sviluppare, perchè a volte preferiamo disegnare prima le nostre entità, per, poi, costruire il database su di esse.

In questo caso entra in gioco il secondo modo di mappare le classi con le tabelle, che è Model-First. Con questo approccio, creiamo noi le entità nel designer e poi utilizziamo il designer stesso per generare un database in base alle entità. Abbiamo, così, la libertà di disegnare le entità come vogliamo, pur dovendo prendere in cambio alcune limitazioni nel design del database. Ad esempio, se usiamo l'eredità, questa viene persistita con il modello TPT (ovver una tabella per ogni classe nella gerarchia di ereditarietà) e questo non è sempre ciò che vogliamo. Inoltre, creare una classe nel designer è un processo abbastanza lento e noioso, quindi una modo migliore di creare le classi potrebbe aiutarci.

Creare le classi di dominio con Code-First

Il modo migliore per creare le classi di dominio per poi mapparle verso le tabelle nel database è senza dubbio Code-First. Grazie a questo nuovo approccio, possiamo scrivere il codice delle nostre classi di dominio e fare in modo che queste siano mappate verso le tabelle del database, attraverso convenzioni, senza codice aggiuntivo e senza utilizzare il designer.

Ovviamente, un esempio è meglio di mille parole: per capire bene come funziona Code-First partiamo dal codice. Il primo passo per utilizzare questo modello è scaricare la Feature CTP a questo indirizzo ed installarla. Una volta fatto questo, dobbiamo creare un progetto (una Console Application, in questo caso) e aggiungere un riferimento agli assembly EntityFramework.dll (introdotto dalla CTP) e System.Data.Entity.dll. L'ultimo passo consiste nel creare le nostre entity. Supponendo di avere un dominio con le entity Order, Customer e OrderDetail, più un complex type AddressInfo con le informazioni di un indirizzo, il codice per creare le classi è il seguente:

namespace CodeFirst
{
  public class Customer
  {
    public int ID { get; set; }
    public string Name { get; set; }
  }

  public class Order
  {
    public int ID { get; set; }
    public DateTime OrderDate { get; set; }
    public DateTime? ShippingDate { get; set; }
    public AddressInfo ShippingAddress { get; set; }
    public int CustomerID { get; set; }
    public Customer Customer { get; set; }
    public ICollection<OrderDetail> Details { get; set; }
  }

  public class AddressInfo
  {
    public string Address { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
  }

  public class OrderDetail
  {
    public int ID { get; set; }
    public int OrderID { get; set; }
    public int ProductID { get; set; }
    public int UnitPrice { get; set; }
    public int Quantity { get; set; }
  }
}

Come si vede dal codice, le classi sono POCO e non hanno alcun riferimento al loro mapping con il database. Fin qui, in realtà, non abbiamo ancora niente di nuovo. Il bello sta per arrivare.

Creare la classe di contesto con Code-First

Come sappiamo, senza una classe che fa da contesto verso il database, non possiamo accedere ai dati. Creare una classe che fa da contesto è molto semplice, come possiamo vedere nel seguente codice:

namespace CodeFirst
{
  public class OrderContext : DbContext
  {
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<OrderDetail> OrderDetails { get; set; }
  }
}

Questo snippet mostra chiaramente la prima grande novita delle API di accesso ai dati. Infatti, per creare la classe che fa da contesto non usiamo più ObjectContext come classe base, ma DbContext che ne è il sostituto. Inoltre, per esporre gli entity set, non usiamo più ObjectSet<T>, ma DbSet<T>. DbSet<T< e DbContext offrono un set di metodi e proprietà ridisegnati e semplificati, come abbiamo già visto in precedenza. Analizzeremo queste classi nel resto dell'articolo. Ora vediamo come gestire il database.

Gestire il database con Code-First

Abbiamo la classe di contesto e le classi di dominio, dobbiamo, quindi, generare il database. Abbiamo diverse modalità per dire al contesto quale database utilizzare, ma consiglio caldamente di utilizzare il file di configurazione, aggiungendo la seguente stringa di connessione:

<connectionStrings>
  <add connectionString="database=CodeFirst;server=(local);integrated security=SSPI" name="OrderContext" providerName="System.Data.SqlClient"/>
</connectionStrings>

Questa stringa di connessione rappresenta un cambiamento rispetto al passato, poiché non c'è alcun riferimento ai file dell'EDM di Entity Framework. Questo non è necessario in quanto, internamente, la classe DbContext crea un EDM in base alle classi che abbiamo definito. Quello che è importante è che l'attributo name della stringa di connessione corrisponda al nome della classe che fa da contesto (possiamo omettere il namespace).
A questo punto, abbiamo dato ad Entity Framework tutte le informazioni necessarie: le classi, il contesto ed il riferimento al database. Tuttavia, non abbiamo ancora creato il database.

Code-First ci permette di gestire anche il ciclo di vita del database nei seguenti modi:

  • creare il database, se non esiste: se il database a cui punta la stringa di connessione non esiste, Code-First lo genera per noi. Ovviamente, l'utente dell'applicazione deve avere i privilegi per poter eseguire questa azione sul database. Le tabelle vengono create in base agli entity set esposti dal contesto ed ognuna di esse ha un campo per ogni proprietà semplice della tabella. Inoltre, in base alle convenzioni, Entity Framework è in grado di intercettare chiavi primarie, foreign key campi obbligatori o meno e di impostare questi attributi sul database;
  • cancellare e ricreare sempre il database: questa opzione è comoda in fase di testing, quando si vuole ripartire sempre da un database pulito. Ovviamente, in produzione e sviluppo questa modalità è sconsigliata, in quanto vengono cancellati dati ed eventuali indici che sono stati aggiunti alle tabelle;
  • non fare nulla: in questo caso, se il database esiste l'applicazione usa il database, in caso contrario, viene generata un'eccezione perché il database non esiste.

La prima opzione è quella di default ed è l'unica che analizzeremo in quest'articolo. Nei prossimi articoli approfondiremo la feature CTP e vedremo come gestire al meglio il database.

A questo punto, non rimane che passare al codice che recupera e persiste oggetti sul database.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

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

Top Ten Articoli

Articoli via e-mail

Iscriviti alla nostra newsletter nuoviarticoli per ricevere via e-mail le notifiche!

In primo piano

I più letti di oggi

In evidenza

Misc