ended ex_042_013

EFCore3_Reforged
Marc CHEVALDONNE 5 years ago
parent 15f4cc64e1
commit 35fe2b6455

@ -413,8 +413,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_039_002_LINQ_to_XML", "e
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_040_001_LINQ_to_Json", "ex_040_001_LINQ_to_Json\ex_040_001_LINQ_to_Json.csproj", "{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_007_EF_CF_One_to_One_FluentAPI", "ex_042_007_EF_CF_One_to_One_FluentAPI\ex_042_007_EF_CF_One_to_One_FluentAPI.csproj", "{4D079A62-B303-4900-BEC1-994BC26BCCA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_008_EF_CF_One_to_Many", "ex_042_008_EF_CF_One_to_Many\ex_042_008_EF_CF_One_to_Many.csproj", "{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_009_EF_CF_One_to_Many_FluentAPI", "ex_042_009_EF_CF_One_to_Many_FluentAPI\ex_042_009_EF_CF_One_to_Many_FluentAPI.csproj", "{2C53F069-6E87-4A36-8915-E0219EBA8BA7}"
@ -580,7 +578,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_011_SinglePropertyNa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_012_OneToOne_conventions", "p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions\ex_042_012_OneToOne_conventions.csproj", "{762C349D-5685-43FA-A077-2F3BDD07C898}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_013_OneToOne_FluentAPI", "p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI\ex_042_013_OneToOne_FluentAPI.csproj", "{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_013_OneToOne_FluentAPI", "p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI\ex_042_013_OneToOne_FluentAPI.csproj", "{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -3488,26 +3486,6 @@ Global
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x64.Build.0 = Release|Any CPU
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.ActiveCfg = Release|Any CPU
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM64.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x64.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x64.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x86.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x86.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|Any CPU.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM64.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM64.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x64.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x64.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x86.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x86.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|ARM.ActiveCfg = Debug|Any CPU
@ -4993,7 +4971,6 @@ Global
{CF1AE501-3518-45CC-94AA-D6DE047EA022} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5}
{26400B99-2FB6-4B5F-BE2B-D15124F2B51D} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5}
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3} = {3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0}
{4D079A62-B303-4900-BEC1-994BC26BCCA2} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{2C53F069-6E87-4A36-8915-E0219EBA8BA7} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{C6EF99F3-959D-4096-8ABE-140596DD14BD} = {593200F9-6D14-43BC-9289-8BB75FAC6552}

@ -1,42 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : CarnetDeSante.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using System;
namespace ex_042_007_EF_CF_One_to_One_FluentAPI
{
/// <summary>
/// CarnetDeSante est une classe POCO, i.e. Plain Old CLR Object
/// Elle a une relation 1-1 avec la classe Nounours via la propriété Owner.
/// </summary>
public class CarnetDeSante
{
public Guid UniqueId
{
get; set;
}
public DateTime LastModified
{
get; set;
}
public virtual Nounours Owner
{
get; set;
}
public override string ToString()
{
return $"{UniqueId} : carnet de {Owner.Nom}, modifié la dernière fois le {LastModified.ToString("d")}";
}
}
}

@ -1,79 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : DbContextInitializer.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
namespace ex_042_007_EF_CF_One_to_One_FluentAPI
{
/// <summary>
/// initialiseur de stratégies...
/// </summary>
public static class DbContextInitializer
{
/// <summary>
/// remplissage de la base avec des données stubbées.
/// </summary>
/// <param name="context">base à remplir</param>
public static void Seed(NounoursDBEntities context)
{
SetInitializer(context, InitializationStrategies.DropCreateDatabaseAlways);
Nounours chewie = new Nounours { Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 };
Nounours yoda = new Nounours { Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 };
Nounours ewok = new Nounours { Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };
CarnetDeSante carnet1 = new CarnetDeSante { Owner = chewie, LastModified = DateTime.Today };
CarnetDeSante carnet2 = new CarnetDeSante { Owner = yoda, LastModified = new DateTime(1980, 5, 21) };
CarnetDeSante carnet3 = new CarnetDeSante { Owner = ewok, LastModified = new DateTime(1983, 5, 25) };
chewie.Carnet = carnet1;
yoda.Carnet = carnet2;
ewok.Carnet = carnet3;
context.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
context.Carnets.AddRange(new CarnetDeSante[] { carnet1, carnet2, carnet3 });
context.SaveChanges();
}
/// <summary>
/// les différentes stratégies de création de la base
/// </summary>
public enum InitializationStrategies
{
CreateDatabaseIfNotExists,
DropCreateDatabaseIfModelChanges,
DropCreateDatabaseAlways
}
public static void SetInitializer(DbContext context, InitializationStrategies strategy)
{
switch(strategy)
{
//par défaut : crée la base seulement si elle n'existe pas
default:
case InitializationStrategies.CreateDatabaseIfNotExists:
context.Database.EnsureCreated();
break;
//recrée la base même si elle existe déjà
case InitializationStrategies.DropCreateDatabaseAlways:
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
break;
//recrée la base seulement si le modèle change : impossible aujourd'hui en Entity Framework Core...
case InitializationStrategies.DropCreateDatabaseIfModelChanges:
throw new NotImplementedException("Le mode DropCreateDatabaseIfModelChanges ne peut pas encore exister sous Entity Framework Core");
}
}
}
}

@ -1,98 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using System;
namespace ex_042_007_EF_CF_One_to_One_FluentAPI
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation 1-1 avec la classe CarnetDeSante via la propriété Carnet.
/// </summary>
public class Nounours
{
public Guid UniqueId
{
get; set;
}
public virtual CarnetDeSante Carnet { get; set; }
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,66 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_007_EF_CF_One_to_One_FluentAPI
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient deux DbSet<T> pour permettre de réaliser des opérations CRUD sur les types T, ici Nounours et CarnetDeSante.
/// </summary>
public class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
public virtual DbSet<CarnetDeSante> Carnets { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_007_EF_CF_One_to_One_FluentAPI.Nounours.mdf;Trusted_Connection=True;");
}
/// <summary>
/// méthode appelée lors de la création du modèle.
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//création de la table TableNounours
modelBuilder.Entity<Nounours>().ToTable("TableNounours"); //nom de la table
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId); //définition de la clé primaire
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId)
.ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
.HasMaxLength(256); //définition de la colonne Nom
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance"); //changement du nom de la colonne Naissance
//modelBuilder.Entity<Nounours>().Property(n => n.Carnet).IsRequired();
//note : la colonne NbPoils n'est pas changée : utilisation des conventions EF
//création de la table "Carnets"
modelBuilder.Entity<CarnetDeSante>().ToTable("Carnets"); // nom de la table
modelBuilder.Entity<CarnetDeSante>().HasKey(c => c.UniqueId); //définition de la clé primaire
//modelBuilder.Entity<CarnetDeSante>().Property(c => c.Owner).IsRequired();
modelBuilder.Entity<CarnetDeSante>().Property(c => c.UniqueId)
.ValueGeneratedNever(); // définition du mode de génération de la clé : pas de génération automatique
//note : la colonne LastModified n'est pas touchée : utilisation des conventions EF
//on précise qu'il y a une relation entre CarnetDeSante et Nounours
modelBuilder.Entity<Nounours>() //l'entité Nounours...
.HasOne(n => n.Carnet) //a une propriété obligatoire Carnet...
.WithOne(c => c.Owner) //reliée à la propriété Owner du Carnet...
.HasForeignKey<CarnetDeSante>(c => c.UniqueId);//dont la propriété UniqueId est une Foreign Key
//remplace la ForeignKey
base.OnModelCreating(modelBuilder);
}
}
}

@ -1,75 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-18
//
// ========================================================================
using System;
using static System.Console;
namespace ex_042_007_EF_CF_One_to_One_FluentAPI
{
public class Program
{
/// <summary>
/// Cet exemple montre comment construire une relation 1-1 dans la base de données en utilisant la Fluent API.
/// On préférera cette solution par exemple lorsque nous n'avons pas accès à la classe Model et que nous ne pouvons donc pas utiliser les conventions de nommage
/// comme dans l'exemple précédent.
///
/// on utilise les données stubbées de MyStubDataInitializationStrategy
/// On affiche les Nounours et les Carnets de santé.
/// Constatez que les identifiants sont bien les mêmes à cause de la relation 1-1.
/// </summary>
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
WriteLine("nounours : ");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
}
WriteLine("carnets de santé : ");
foreach (var c in db.Carnets)
{
WriteLine($"\t{c}");
}
}
}
catch (NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (Exception e)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_007_EF_CF_One_to_One_FluentAPI)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>ex_042_007_EF_CF_One_to_One_FluentAPI</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>ex_042_007_EF_CF_One_to_One_FluentAPI</PackageId>
<RuntimeFrameworkVersion>3.0.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

@ -32,9 +32,10 @@ Ce chapitre s'attardera sur le lien entre le mod
* [**ex_042_008 : Data Seeding before Entity Framework 2.1**](ex_042_008_DataSeeding_before_EF2.1) : explique comment utiliser un stub (méthode qui était recommandée avant EF Core 2.1)
* [**ex_042_009 : Data Seeding**](ex_042_009_DataSeeding) : explique comment utiliser un stub (méthode recommandée depuis EF Core 2.1)
* [**Relationships**](Relationships.md) : en cliquant [ici](Relationships.md), vous aurez plus de détails sur les relations entre entités
* [**ex_042_010 : Single Property navigation with data annotations**](ex_042_010_SinglePropertyNavigation_conventions) : montre comment une relation d'association est traduite par **EF Core** lorsque cette association est unidirectionnelle entre deux entités, en utilisant les conventions d'écriture et/ou les annotations de données.
* [**ex_042_011 : Single Property navigation with Fluent API**](ex_042_011_SinglePropertyNavigation_FluentAPI) : montre comment une relation d'association est traduite par **EF Core** lorsque cette association est unidirectionnelle entre deux entités, en utilisant la *FLuent API*.
* [**ex_042_012 : One To One with data annotations**](ex_042_012_OneToOne_conventions) : montre comment une relation d'association *One To One* est traduite par **EF Core** lorsque cette association est bidirectionnelle entre deux entités, en utilisant l'*annotation de données*.
* [**ex_042_010 : Single Property navigation with data annotations**](ex_042_010_SinglePropertyNavigation_conventions) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant les conventions d'écriture et/ou les annotations de données.
* [**ex_042_011 : Single Property navigation with Fluent API**](ex_042_011_SinglePropertyNavigation_FluentAPI) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant la *FLuent API*.
* [**ex_042_012 : One To One with data annotations**](ex_042_012_OneToOne_conventions) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant l'*annotation de données*.
* [**ex_042_013 : One To One with Fluent API**](ex_042_013_OneToOne_FluentAPI) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant la *FluentAPI*.
3. *Schemas and migrations* :
Le but de ce chapitre sera de vous montrer comment garder votre modèle et votre base de données synchronisés.
4. *Querying (LINQ to SQL) and saving data* :

@ -34,7 +34,6 @@ namespace ex_042_013_OneToOne_FluentAPI
//création de la table "Carnets"
modelBuilder.Entity<CarnetDeSante>().ToTable("Carnets"); // nom de la table
modelBuilder.Entity<CarnetDeSante>().HasKey(c => c.UniqueId); //définition de la clé primaire
//modelBuilder.Entity<CarnetDeSante>().Property(c => c.Owner).IsRequired();
modelBuilder.Entity<CarnetDeSante>().Property(c => c.UniqueId)
.ValueGeneratedNever(); // définition du mode de génération de la clé : pas de génération automatique
//note : la colonne LastModified n'est pas touchée : utilisation des conventions EF

@ -8,7 +8,7 @@ namespace ex_042_013_OneToOne_FluentAPI
public class Program
{
/// <summary>
/// Cet exemple montre comment construire une relation 1-1 dans la base de données en utilisant les conventions de nommage Entity Framework.
/// Cet exemple montre comment construire une relation 1-1 dans la base de données en utilisant la Fluent API d'Entity Framework.
/// </summary>
static void Main(string[] args)
{

@ -1,12 +1,12 @@
# ex_042_012_OneToOne_conventions
# ex_042_013_OneToOne_FluentAPI
*20/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple montre comment réaliser une relation *One To One* entre deux entités avec *Entity Framework Core* et l'*annotation de données*.
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
[**ex_042_013 : One To One with Fluent API**](../ex_042_013_OneToOne_FluentAPI)
Cet exemple montre comment réaliser une relation *One To One* entre deux entités avec *Entity Framework Core* et la *Fluent API*.
Une version équivalente réalisée avec les *annotations de données* est disponible dans l'exemple
[**ex_042_012 : One To One with data annotations**](../ex_042_012_OneToOne_conventions)
---
@ -23,7 +23,7 @@ Une version équivalente réalisée avec la *Fluent API* est disponible dans l'e
Un ```Nounours``` contient différentes propriétés (```Nom```, ```DateDeNaissance```, ```NbPoils```) dont ```Carnet``` de type ```CarnetDeSante```.
Un ```CarnetDeSante``` possède une propriété de type ```DateTime``` (```LastModified```), et une propriété ```Owner``` de type ```Nounours```.
On a donc bien une relation *One To One* puisqu'un ```Nounours``` possède un ```CarnetDeSante``` et qu'un ```CarnetDeSante``` possède un ```Nounours```.
<img src="./readme_files/ex_042_012_classDiagram.svg"/>
<img src="../ex_042_012_OneToOne_conventions/readme_files/ex_042_012_classDiagram.svg"/>
Ce qu'il faut noter :
* ```Nounours``` possède une association vers ```CarnetDeSante```
@ -34,18 +34,8 @@ public CarnetDeSante Carnet { get; set; }
```csharp
public Nounours Owner { get; set; }
```
* ```Nounours``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _annotation de données_) : ```[Key]```.
* Cet identifiant est généré par la base : ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]```.
* ```CarnetDeSante``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _annotation de données_) : ```[Key]```.
* Cet identifiant fait référence à une clé étrangère qui est la clé primaire associée à l'entité pointée par sa propriété ```Owner``` : ```ForeignKey("Owner")```.
```csharp
[Key, ForeignKey("Owner")]
public int UniqueId
{
get; set;
}
```
* En conséquence, l'identifiant unique ```UniqueId``` de ```CarnetDeSante``` aura la même valeur que l'```UniqueId``` d'une entité ```Nounours```.
* ```Nounours``` possède un identifiant unique ```UniqueId```.
* ```CarnetDeSante``` possède un identifiant unique ```UniqueId```.
### La classe ```NounoursDBEntities```
@ -56,9 +46,80 @@ public int UniqueId
public DbSet<Nounours> NounoursSet { get; set; }
public DbSet<CarnetDeSante> Carnets { get; set; }
```
##### Quelques explications supplémentaires :
* La classe réécrit ensuite la méthode ```OnModelCreating``` :
```csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//création de la table TableNounours
modelBuilder.Entity<Nounours>().ToTable("TableNounours"); //nom de la table
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId); //définition de la clé primaire
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId)
.ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
.HasMaxLength(256); //définition de la colonne Nom
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date"); //changement du nom de la colonne Naissance
//création de la table "Carnets"
modelBuilder.Entity<CarnetDeSante>().ToTable("Carnets"); // nom de la table
modelBuilder.Entity<CarnetDeSante>().HasKey(c => c.UniqueId); //définition de la clé primaire
modelBuilder.Entity<CarnetDeSante>().Property(c => c.UniqueId)
.ValueGeneratedNever(); // définition du mode de génération de la clé : pas de génération automatique
//note : la colonne LastModified n'est pas touchée : utilisation des conventions EF
//on précise qu'il y a une relation entre CarnetDeSante et Nounours
modelBuilder.Entity<Nounours>() //l'entité Nounours...
.HasOne(n => n.Carnet) //a une propriété obligatoire Carnet...
.WithOne(c => c.Owner) //reliée à la propriété Owner du Carnet...
.HasForeignKey<CarnetDeSante>(c => c.UniqueId);//dont la propriété UniqueId est une Foreign Key
//remplace la ForeignKey
base.OnModelCreating(modelBuilder);
}
```
Voyons cette méthode plus en détails.
Tout d'abord, il y a la définition des détails de la table de ```Nounours``` :
* le changement de nom de la table :
```csharp
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
```
* la définition de la clé primaire :
```csharp
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId)
.ValueGeneratedOnAdd();
```
* définition de contraintes sur les colonnes de ```Nounours``` :
```csharp
modelBuilder.Entity<Nounours>().Property(n => n.Nom)
.IsRequired()
.HasMaxLength(256);
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance)
.HasColumnName("Naissance")
.HasColumnType("date");
```
On continue avec la définition des détails de la table de ```CarnetDeSante``` :
* le changement de nom de la table :
```csharp
modelBuilder.Entity<CarnetDeSante>().ToTable("Carnets");
```
* la définition de la clé primaire (*notez qu'on ne demande pas ici à la base de générer la clé, puisqu'on va utiliser une clé étrangère*) :
```csharp
modelBuilder.Entity<CarnetDeSante>().HasKey(c => c.UniqueId);
modelBuilder.Entity<CarnetDeSante>().Property(c => c.UniqueId)
.ValueGeneratedNever();
```
On s'intéresse enfin à la **relation _One To One_**, où l'on précise qu'une entité ```Nounours``` possède une association vers l'entité de type ```CarnetDeSante``` grâce à la propriété ```Carnet``` (```.HasOne(n => n.Carnet)```).
Cette entité ```CarnetDeSante``` possède elle-même une association vers une entité de type ```Nounours``` grâce à sa propriété ```Owner``` (```.WithOne(c => c.Owner)```),
et on précise que cette entité ```CarnetDeSante``` voit sa propriété ```UniqueId``` utilisée comme clé étrangère (```.HasForeignKey<CarnetDeSante>(c => c.UniqueId)```). Elle automatiquement reliée à la clé primaire de ```Nounours```.
```csharp
modelBuilder.Entity<Nounours>() //l'entité Nounours...
.HasOne(n => n.Carnet) //a une propriété obligatoire Carnet...
.WithOne(c => c.Owner) //reliée à la propriété Owner du Carnet...
.HasForeignKey<CarnetDeSante>(c => c.UniqueId);//dont la propriété UniqueId est une Foreign Key
```
Pour pouvoir relier une entité ```Nounours``` à une entité ```CarnetDeSante``` de manière bidirectionnelle, les propriétés ```Nounours.Carnet``` et ```CarnetDeSante.Owner``` sont suffisantes, à condition qu'une clé étrangère soit déclarée et utilisée.
C'est le cas grâce à l'annotation de données ```ForeignKey("Owner")``` qui s'occupe de définir de quelle manière les deux entités sont reliées.
C'est le cas grâce au code précédent qui s'occupe de définir de quelle manière les deux entités sont reliées.
### La classe ```StubbedContext```
@ -147,7 +208,7 @@ Pour tester cette application, n'oubliez pas les commandes comme présentées da
* Ouvrez la *Console du Gestionnaire de package* sous Windows ou le *Terminal* sous MacOSX, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
* Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
```
cd .\p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions
cd .\p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI
```
*Note*:
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
@ -159,14 +220,14 @@ cd .\p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions
* Migration :
```
dotnet ef migrations add ex_042_012 --context StubbedContext
dotnet ef migrations add ex_042_013 --context StubbedContext
```
* Création de la table :
```
dotnet ef database update --context StubbedContext
```
* Génération et exécution
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_012_OneToOne_conventions**.
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_013_OneToOne_FluentAPI**.
* Le résultat de l'exécution va ressembler à :
```
@ -197,7 +258,7 @@ Contenu de la base (carnets de santé) :
## Comment exécuter cet exemple sans le stub ?
Il suffit de faire exactement comme dans le paragraphe précédent, mais en choisissant le contexte ```NounoursDBEntities``` à la place de ```StubbedContext``` :
```
dotnet ef migrations add ex_042_012 --context NounoursDBEntities
dotnet ef migrations add ex_042_013 --context NounoursDBEntities
dotnet ef database update --context NounoursDBEntities
```
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les 3 nounours du stub.
@ -206,11 +267,11 @@ Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aur
Pour vérifier le contenu de votre base SQLite, vous pouvez utiliser le programme *DB Browser* :
* Rendez-vous sur la page : https://sqlitebrowser.org/dl/ et téléchargez le programme *DB Browser*.
* Lancez *DB Browser for SQLite*
* Glissez-déposez au milieu de la fenêtre de *DB Browser for SQLite* le fichier *ex_042_012_OneToOne_conventions.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_012_OneToOne_conventions.csproj*.
![DB Browser for SQLite](./readme_files/dbbrowser01.png)
* Glissez-déposez au milieu de la fenêtre de *DB Browser for SQLite* le fichier *ex_042_013_OneToOne_FluentAPI.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_012_OneToOne_conventions.csproj*.
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser01.png)
* Choisissez ensuite l'onglet *Parcourir les données*
* Observez les résultats obtenus des deux tables
![DB Browser for SQLite](./readme_files/dbbrowser02.png)
![DB Browser for SQLite](./readme_files/dbbrowser03.png)
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser02.png)
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser03.png)

@ -19,7 +19,7 @@
* V 010, 011 shadow properties
* V 010, 011, single navigation property
* cascade delete
* 012 one to one
* V 012 013 one to one
* one to many
* many to many
* dictionaries

Loading…
Cancel
Save