Entity Framework - Premier exemple

Définissons un modèle très simple en utilisant des classes. Nous les définissons simplement dans le fichier Program.cs, mais dans une application du monde réel, vous diviserez vos classes en fichiers séparés et potentiellement en un projet distinct. Voici un modèle de données que nous allons créer en utilisant l'approche Code First.

Créer un modèle

Ajoutez les trois classes suivantes dans le fichier Program.cs à l'aide du code suivant pour 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 propriété ID deviendra la colonne de clé primaire de la table de base de données correspondant à cette classe.

  • La propriété Enrollments est une propriété de navigation. Les propriétés de navigation contiennent d'autres entités liées à cette entité.

  • Dans ce cas, la propriété Enrollments d'une entité Student contiendra toutes les entités Enrollment qui sont liées à cette entité Student.

  • Les propriétés de navigation sont généralement définies comme virtuelles afin de pouvoir tirer parti de certaines fonctionnalités d'Entity Framework telles que le chargement différé.

  • Si une propriété de navigation peut contenir plusieurs entités (comme dans les relations plusieurs-à-plusieurs ou un-à-plusieurs), son type doit être une liste dans laquelle des entrées peuvent être ajoutées, supprimées et mises à jour, telles que ICollection.

Voici l'implémentation de 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 propriété Enrollments est une propriété de navigation. Une entité de cours peut être liée à un nombre quelconque d'entités d'inscription.

Voici l'implémentation de la classe d'inscription et de l'énumération.

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 propriété EnrollmentID sera la clé primaire.

  • La propriété Grade est une énumération. Le point d'interrogation après la déclaration de type Grade indique que la propriété Grade est nullable.

  • Une note nulle est différente d'une note zéro. Null signifie qu'une note n'est pas connue ou n'a pas encore été attribuée.

  • Les propriétés StudentID et CourseID sont des clés étrangères et les propriétés de navigation correspondantes sont Student et Course.

  • Une entité Inscription est associée à une entité Etudiant et à une entité Cours, de sorte que la propriété ne peut contenir qu'une seule entité Etudiant et Cours.

Créer un contexte de base de données

La classe principale qui coordonne la fonctionnalité Entity Framework pour un modèle de données donné est la classe de contexte de base de données qui permet d'interroger et d'enregistrer des données. Vous pouvez créer cette classe en dérivant de la classe DbContext et en exposant un DbSet typé pour chaque classe de notre modèle. Voici l'implémentation sur la classe MyContext, qui est dérivée de la 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; }
}

Voici le code complet dans le fichier 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; }
   }

}

Le code ci-dessus est tout ce dont nous avons besoin pour commencer à stocker et à récupérer des données. Ajoutons quelques données, puis récupérons-les. Voici le code de la méthode 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();
   }
}

Lorsque le code ci-dessus est exécuté, vous recevrez la sortie suivante.

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

Maintenant, la question qui me vient à l'esprit est de savoir où se trouvent les données et la base de données dans lesquelles nous avons ajouté des données, puis les avons extraites de la base de données. Par convention, DbContext a créé une base de données pour vous.

  • Si une instance SQL Express locale est disponible, Code First a créé la base de données sur cette instance.

  • Si SQL Express n'est pas disponible, Code First essaiera d'utiliser LocalDb.

  • La base de données est nommée d'après le nom qualifié complet du contexte dérivé.

Dans notre cas, l'instance SQL Express est disponible et le nom de la base de données est EFCodeFirstDemo.MyContext, comme illustré dans l'image suivante.

  • Ce ne sont que les conventions par défaut et il existe différentes manières de modifier la base de données utilisée par Code First.

  • Comme vous pouvez le voir dans l'image ci-dessus, il a créé des tables Etudiants, Cours et Inscriptions et chaque table contient des colonnes avec le type de données et la longueur appropriés.

  • Les noms de colonne et le type de données correspondent également aux propriétés des classes de domaine respectives.

Initialisation de la base de données

Dans l'exemple ci-dessus, nous avons vu que Code First crée automatiquement une base de données, mais si vous souhaitez changer le nom de la base de données et du serveur, voyons comment Code First décide du nom et du serveur de la base de données lors de l'initialisation d'une base de données. Jetez un œil au diagramme suivant.

Vous pouvez définir le constructeur de base de la classe de contexte des manières suivantes.

  • Aucun paramètre
  • Nom de la base de données
  • Nom de la chaîne de connexion

Aucun paramètre

Si vous spécifiez le constructeur de base de la classe de contexte sans aucun paramètre, comme indiqué dans l'exemple ci-dessus, alors le framework d'entité créera une base de données dans votre serveur SQLEXPRESS local avec un nom {Namespace}. {Context class name}.

Dans l'exemple ci-dessus, la base de données qui est créée automatiquement porte le nom EFCodeFirstDemo.MyContext. Si vous regardez le nom, vous constaterez que EFCodeFirstDemo est l'espace de noms et MyContext est le nom de la classe de contexte, comme indiqué dans le code suivant.

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; }
}

Nom de la base de données

Si vous passez le nom de la base de données en tant que paramètre dans un constructeur de base de la classe de contexte, alors Code First créera à nouveau une base de données automatiquement, mais cette fois le nom sera celui passé en paramètre dans le constructeur de base sur le serveur de base de données SQLEXPRESS local .

Dans le code suivant, MyContextDB est spécifié en tant que paramètre dans le constructeur de base. Si vous exécutez votre application, la base de données portant le nom MyContextDB sera créée sur votre serveur SQL local.

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; }
}

Nom de la chaîne de connexion

Il s'agit d'un moyen simple d'indiquer à DbContext d'utiliser un serveur de base de données autre que SQL Express ou LocalDb. Vous pouvez choisir de mettre une chaîne de connexion dans votre fichier app.config.

  • Si le nom de la chaîne de connexion correspond au nom de votre contexte (avec ou sans qualification d'espace de noms), alors il sera trouvé par DbContext lorsque le paramètre moins constructeur est utilisé.

  • Si le nom de la chaîne de connexion est différent du nom de votre contexte, vous pouvez indiquer à DbContext d'utiliser cette connexion en mode Code First en passant le nom de la chaîne de connexion au constructeur 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; }
}
  • Dans le code ci-dessus, un extrait de la chaîne de connexion de classe de contexte est spécifié en tant que paramètre dans le constructeur de base.

  • Le nom de la chaîne de connexion doit commencer par "name =" sinon, il le considérera comme un nom de base de données.

  • Ce formulaire indique explicitement que vous vous attendez à ce que la chaîne de connexion soit trouvée dans votre fichier de configuration. Une exception sera levée si une chaîne de connexion avec le nom donné n'est pas trouvée.

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Le nom de la base de données dans la chaîne de connexion dans app.config est EFMyContextDB. CodeFirst créera un nouveauEFMyContextDB base de données ou utiliser existant EFMyContextDB base de données sur SQL Server local.

Classes de domaine

Jusqu'à présent, nous avons simplement laissé EF découvrir le modèle en utilisant ses conventions par défaut, mais il y aura des moments où nos classes ne suivent pas les conventions et nous devons être en mesure d'effectuer une configuration supplémentaire. Mais vous pouvez remplacer ces conventions en configurant vos classes de domaine pour fournir à EF les informations dont il a besoin. Il existe deux options pour configurer vos classes de domaine -

  • Annotations de données
  • API Fluent

Annotations de données

DataAnnotations est utilisé pour configurer vos classes qui mettront en évidence les configurations les plus couramment nécessaires. Les DataAnnotations sont également comprises par un certain nombre d'applications .NET, telles que ASP.NET MVC, qui permettent à ces applications d'exploiter les mêmes annotations pour les validations côté client.

Voici les annotations de données utilisées en classe d'élèves.

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 Fluent

La plupart des configurations de modèle peuvent être effectuées à l'aide d'annotations de données simples. L'API Fluent est un moyen avancé de spécifier la configuration du modèle qui couvre tout ce que les annotations de données peuvent faire, en plus d'une configuration plus avancée impossible avec les annotations de données. Les annotations de données et l'API fluent peuvent être utilisées ensemble.

Pour accéder à l'API fluent, vous remplacez la méthode OnModelCreating dans DbContext. Renommons maintenant le nom de la colonne dans la table des étudiants de FirstMidName en FirstName, comme indiqué dans le code suivant.

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; }
}