Entity Framework - Primo esempio

Definiamo un modello molto semplice utilizzando le classi. Li stiamo solo definendo nel file Program.cs ma in un'applicazione del mondo reale dividerete le vostre classi in file separati e potenzialmente in un progetto separato. Di seguito è riportato un modello di dati che creeremo utilizzando l'approccio Code First.

Crea modello

Aggiungere le tre classi seguenti nel file Program.cs utilizzando il codice seguente per la classe Student.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • La proprietà ID diventerà la colonna della chiave primaria della tabella del database che corrisponde a questa classe.

  • La proprietà Enrollments è una proprietà di navigazione. Le proprietà di navigazione contengono altre entità correlate a questa entità.

  • In questo caso, la proprietà Enrollments di un'entità Student conterrà tutte le entità Enrollment correlate a tale entità Student.

  • Le proprietà di navigazione sono in genere definite come virtuali in modo che possano sfruttare alcune funzionalità di Entity Framework come il caricamento lento.

  • Se una proprietà di navigazione può contenere più entità (come nelle relazioni molti-a-molti o uno-molti), il suo tipo deve essere un elenco in cui è possibile aggiungere, eliminare e aggiornare voci, come ICollection.

Di seguito è riportata l'implementazione per la classe Course.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

La proprietà Enrollments è una proprietà di navigazione. Un'entità Corso può essere correlata a qualsiasi numero di entità Iscrizione.

Di seguito è riportata l'implementazione per la classe Enrollment e l'enumerazione.

public enum Grade {
   A, B, C, D, F
}

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}
  • La proprietà EnrollmentID sarà la chiave primaria.

  • La proprietà Grade è un'enumerazione. Il punto interrogativo dopo la dichiarazione del tipo Grade indica che la proprietà Grade è nullable.

  • Un voto nullo è diverso da un voto zero. Null significa che un voto non è noto o non è stato ancora assegnato.

  • Le proprietà StudentID e CourseID sono chiavi esterne e le proprietà di navigazione corrispondenti sono Student e Course.

  • Un'entità Enrollment è associata a un'entità Student e un'entità Course, quindi la proprietà può contenere solo un'entità Student e Course.

Crea contesto database

La classe principale che coordina la funzionalità di Entity Framework per un dato modello di dati è la classe del contesto del database che consente di interrogare e salvare i dati. È possibile creare questa classe derivando dalla classe DbContext ed esponendo un DbSet tipizzatoper ogni classe nel nostro modello. Di seguito è riportata l'implementazione sulla classe MyContext, che è derivata dalla classe DbContext.

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Di seguito è riportato il codice completo nel file Program.cs.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   public enum Grade {
      A, B, C, D, F
   }

   public class Enrollment {
      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Grade? Grade { get; set; }
		
      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Il codice sopra è tutto ciò di cui abbiamo bisogno per iniziare a memorizzare e recuperare i dati. Aggiungiamo alcuni dati e poi recuperiamoli. Di seguito è riportato il codice nel metodo principale.

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
		
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
		
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Quando il codice precedente viene eseguito, riceverai il seguente output.

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

Ora la domanda che mi viene in mente è: dove sono i dati e il database in cui abbiamo aggiunto alcuni dati e poi li abbiamo recuperati dal database. Per convenzione, DbContext ha creato un database per te.

  • Se è disponibile un'istanza SQL Express locale, Code First ha creato il database su tale istanza.

  • Se SQL Express non è disponibile, Code First proverà a utilizzare LocalDb.

  • Il database prende il nome dal nome completo del contesto derivato.

Nel nostro caso, è disponibile l'istanza di SQL Express e il nome del database è EFCodeFirstDemo.MyContext come mostrato nell'immagine seguente.

  • Queste sono solo le convenzioni predefinite e sono disponibili vari modi per modificare il database utilizzato da Code First.

  • Come puoi vedere nell'immagine sopra, ha creato tabelle Studenti, Corsi e Iscrizioni e ogni tabella contiene colonne con tipo di dati e lunghezza appropriati.

  • I nomi delle colonne e il tipo di dati corrispondono anche alle proprietà delle rispettive classi di dominio.

Inizializzazione del database

Nell'esempio sopra, abbiamo visto che Code First crea automaticamente un database, ma se vuoi cambiare il nome del database e del server, vediamo come Code First decide il nome del database e il server durante l'inizializzazione di un database. Dai un'occhiata al diagramma seguente.

È possibile definire il costruttore di base della classe di contesto nei seguenti modi.

  • Nessun parametro
  • Nome del database
  • Nome stringa di connessione

Nessun parametro

Se si specifica il costruttore di base della classe di contesto senza alcun parametro come mostrato nell'esempio precedente, il framework di entità creerà un database nel server SQLEXPRESS locale con un nome {Namespace}. {Context class name}.

Nell'esempio precedente, il database che viene creato automaticamente ha il nome EFCodeFirstDemo.MyContext. Se guardi il nome, scoprirai che EFCodeFirstDemo è lo spazio dei nomi e MyContext è il nome della classe di contesto come mostrato nel codice seguente.

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Nome del database

Se si passa il nome del database come parametro in un costruttore di base della classe di contesto, Code First creerà di nuovo automaticamente un database, ma questa volta il nome sarà quello passato come parametro nel costruttore di base sul server di database SQLEXPRESS locale .

Nel codice seguente, MyContextDB viene specificato come parametro nel costruttore di base. Se esegui la tua applicazione, il database con il nome MyContextDB verrà creato nel tuo server SQL locale.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Nome stringa di connessione

Questo è un modo semplice per indicare a DbContext di utilizzare un server di database diverso da SQL Express o LocalDb. Puoi scegliere di inserire una stringa di connessione nel tuo file app.config.

  • Se il nome della stringa di connessione corrisponde al nome del contesto (con o senza la qualifica dello spazio dei nomi), verrà trovato da DbContext quando viene utilizzato il costruttore del parametro less.

  • Se il nome della stringa di connessione è diverso dal nome del contesto, è possibile indicare a DbContext di utilizzare questa connessione in modalità Code First passando il nome della stringa di connessione al costruttore DbContext.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • Nel codice precedente, lo snippet della stringa di connessione della classe di contesto è specificato come parametro nel costruttore di base.

  • Il nome della stringa di connessione deve iniziare con "name =" altrimenti lo considererà come un nome di database.

  • Questo modulo rende esplicito che ti aspetti che la stringa di connessione venga trovata nel tuo file di configurazione. Verrà generata un'eccezione se non viene trovata una stringa di connessione con il nome specificato.

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Il nome del database nella stringa di connessione in app.config è EFMyContextDB. CodeFirst creerà un nuovo fileEFMyContextDB database o usa esistente EFMyContextDB database in SQL Server locale.

Classi di dominio

Finora abbiamo lasciato che EF scoprisse il modello usando le sue convenzioni predefinite, ma ci saranno momenti in cui le nostre classi non seguono le convenzioni e dobbiamo essere in grado di eseguire ulteriori configurazioni. Ma puoi ignorare queste convenzioni configurando le classi di dominio per fornire a EF le informazioni di cui ha bisogno. Sono disponibili due opzioni per configurare le classi di dominio:

  • Annotazioni dei dati
  • API fluente

Annotazioni dei dati

DataAnnotations viene utilizzato per configurare le classi che evidenzieranno le configurazioni più comunemente necessarie. DataAnnotations sono comprese anche da numerose applicazioni .NET, come ASP.NET MVC, che consentono a queste applicazioni di sfruttare le stesse annotazioni per le convalide sul lato client.

Di seguito sono riportate le annotazioni dei dati utilizzate nella classe dello studente.

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

API fluente

La maggior parte della configurazione del modello può essere eseguita utilizzando semplici annotazioni dei dati. L'API fluente è un modo avanzato per specificare la configurazione del modello che copre tutto ciò che le annotazioni dei dati possono fare, oltre ad alcune configurazioni più avanzate non possibili con le annotazioni dei dati. Le annotazioni dei dati e l'API fluente possono essere utilizzate insieme.

Per accedere all'API fluente, sovrascrivi il metodo OnModelCreating in DbContext. Ora rinominiamo il nome della colonna nella tabella degli studenti da FirstMidName a FirstName come mostrato nel codice seguente.

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}