Compare commits
No commits in common. 'master' and 'EFCore3_Reforged' have entirely different histories.
master
...
EFCore3_Re
@ -1,41 +0,0 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: CI
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: mcr.microsoft.com/dotnet/sdk:6.0
|
||||
volumes:
|
||||
- name: docs
|
||||
path: /docs
|
||||
commands:
|
||||
#- dotnet new tool-manifest
|
||||
#- dotnet tool install --version 5.3.1 Swashbuckle.AspNetCore.Cli
|
||||
- dotnet restore Exemples_coreOnly.sln
|
||||
- dotnet build Exemples_coreOnly.sln -c Release --no-restore
|
||||
- dotnet publish Exemples_coreOnly.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
|
||||
|
||||
# La documentation Doxygen doit être dans le répertoire
|
||||
# Documentation/doxygen
|
||||
# avec le ficher
|
||||
# Documentation/doxygen/Doxyfile contenant
|
||||
# OUTPUT_DIRECTORY = /docs/doxygen
|
||||
- name: generate-and-deploy-docs
|
||||
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer
|
||||
volumes:
|
||||
- name: docs
|
||||
path: /docs
|
||||
commands:
|
||||
- /entrypoint.sh
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
depends_on: [ build ]
|
||||
|
||||
volumes:
|
||||
- name: docs
|
||||
temp: {}
|
@ -1,354 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
Documentation/docusaurus/.docusaurus/*
|
||||
Documentation/docusaurus/build/*
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
|
||||
**/wwwroot/lib/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Mac stuff
|
||||
.DS_Store
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-archive-b17.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-f72.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-tags-docusaurus-cea-list.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-tags-docusaurus-cea.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-tags-tags-bf9.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-tags-welcome-3e7-list.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-blog/default/documentation-mch-samples-net-docusaurus-mchsamples-net-core-fr-blog-tags-welcome-3e7.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-docs/default/category-documentationmchsamples-netdocusaurusmchsamples-net-corefrdocs-tutorialsidebar-category-1-fundamentals-21f.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-docs/default/category-documentationmchsamples-netdocusaurusmchsamples-net-corefrdocs-tutorialsidebar-category-2-model-c16.json
|
||||
Documentation/docusaurus/.docusaurus/docusaurus-plugin-content-docs/default/category-documentationmchsamples-netdocusaurusmchsamples-net-corefrdocs-tutorialsidebar-category-entity-framework-core-cc9.json
|
@ -1,41 +0,0 @@
|
||||
# Website
|
||||
|
||||
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
$ yarn
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
|
||||
### Deployment
|
||||
|
||||
Using SSH:
|
||||
|
||||
```
|
||||
$ USE_SSH=true yarn deploy
|
||||
```
|
||||
|
||||
Not using SSH:
|
||||
|
||||
```
|
||||
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||
```
|
||||
|
||||
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
slug: first-blog-post
|
||||
title: This Page is alive!
|
||||
authors: [codeLord]
|
||||
tags: [welcome, docusaurus]
|
||||
---
|
||||
|
||||
Here is the beginning of this page, giving you access to information about C# .NET, Entity Framework, xUnit, etc.
|
||||
Have fun!
|
@ -1,5 +0,0 @@
|
||||
codeLord:
|
||||
name: Code Lord
|
||||
title: Code#0 Core Team
|
||||
url: https://www.linkedin.com/in/marc-chevaldonn%C3%A9-8902a0205/
|
||||
image_url: https://github.com/pardaillanLeRouge.png
|
@ -1,188 +0,0 @@
|
||||
---
|
||||
sidebar_label: '1.1. Connection Strings'
|
||||
sidebar_position: 1
|
||||
description: "utiliser une chaîne de connexion SQL Server ou SQLite"
|
||||
---
|
||||
|
||||
# ex_041_001_ConnectionStrings
|
||||
|
||||
*31/12/2019 ⋅ Marc Chevaldonné*
|
||||
*Dernière mise à jour : 09/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_041_001_ConnectionStrings)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple a pour but de présenter les chaîne de connexion (*connection strings*).
|
||||
|
||||
* Les *connection strings* servent à se connecter à une base de données.
|
||||
* Elles diffèrent en fonction des *providers*
|
||||
* Parfois, elles nécessitent des informations telles que un nom d'utilisateur et un mot de passe qu'on peut vouloir cacher
|
||||
|
||||
Dans cet exemple, j'ai voulu montrer deux *connection strings* : une pour SQL Server et une autre pour SQLite.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Il faut penser à ajouter les NuGet suivants :
|
||||
|
||||
* Microsoft.EntityFrameworkCore : pour le projet en général
|
||||
* Microsoft.EntityFrameworkCore.SqlServer : pour le *provider* SQL Server
|
||||
* Microsoft.EntityFrameworkCore.Sqlite : pour le *provider* SQLite
|
||||
* Microsoft.EntityFrameworkCore.Tools : pour bénéficier des outils de Design, de migrations, etc.
|
||||
|
||||
De plus, pour SQLite, il faut penser également à rajouter
|
||||
dans l'exemple :
|
||||
|
||||
* le chemin du *starting working directory* : on peut le faire par exemple en modifiant le **.csproj** en ajoutant la ligne suivante :
|
||||
|
||||
```xml
|
||||
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
|
||||
```
|
||||
|
||||
## Comment fonctionne l'exemple ?
|
||||
|
||||
Ce projet contient les classes suivantes :
|
||||
|
||||
* ```Nounours``` : elle est la classe du modèle que j'ai faite la plus simple possible. J'expliquerai dans un exemple ultérieure son mode de fonctionnement. Elle contient un ```Id```et un ```Nom``` qui donneront deux colonnes dans une table Nounours avec les mêmes noms.
|
||||
* ```SqlServerContext``` : première classe qui dérive de ```DbContext``` et qui va permettre de réaliser la connexion avec la base de données de type MSSqlServer
|
||||
* ```SQLiteContext``` : deuxième classe qui dérive de ```DbContext```et qui va permettre de réaliser la connexion avec la base de données de type SQLite.
|
||||
|
||||
Dans les deux classes qui dérivent de ```DbContext```, on doit donner une *connection string*. Celle-ci est donnée via la méthode protégée et virtuelle ```OnConfiguring```,
|
||||
via l'instance de ```DbContextOptionsBuilder```, à travers l'une des méthodes d'extension :
|
||||
|
||||
* ```UseSqlServer``` : pour SqlServer, où on peut voir une *connection string* plus ou moins complexe indiquant qu'elle est en local (```Server=(localdb)\mssqllocaldb;```), ainsi que le nom de la base de données (```Database=ex_041_001_ConnectionStrings.Nounours.mdf;```)
|
||||
* ```UseSqlite``` : pour SQLite, où on peut voir le nom de la base de données ```Data Source=ex_041_001_ConnectionStrings.Nounours.db``` qui sera placée par défaut dans le dossier du projet si vous l'exécutez depuis Visual Studio.
|
||||
|
||||
C'est tout ce que cet exemple souhaite mettre en valeur : les chaînes de connexion.
|
||||
|
||||
## Comment générer et exécuter l'exemple ?
|
||||
|
||||
Pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
|
||||
* (**Windows** Visual Studio 2019) : ouvrez la *Console du Gestionnaire de package*, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
|
||||
* (**MacOSX** Visual Studio 2019 For Mac) : ouvrez un Terminal.
|
||||
* Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet, ici :
|
||||
|
||||
```
|
||||
cd .\p08_BDD_EntityFramework\ex_041_001_ConnectionStrings
|
||||
```
|
||||
|
||||
:::info
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
### Migrations
|
||||
|
||||
Normalement, la commande pour effectuer une migration est :
|
||||
|
||||
```
|
||||
dotnet ef migrations add monNomDeMigration
|
||||
```
|
||||
|
||||
mais comme ici, nous sommes dans le cas particulier où nous avons deux contextes, nous devons préciser les noms des ```DbContext```à migrer :
|
||||
|
||||
```
|
||||
dotnet ef migrations add ex_041_001_SqlServer --context SqlServerContext
|
||||
dotnet ef migrations add ex_041_001_SQLite --context SQLiteContext
|
||||
```
|
||||
|
||||
:::info
|
||||
sous MacOSX, n'utilisez que SQLite, soit :*
|
||||
|
||||
```
|
||||
dotnet ef migrations add ex_041_001_SQLite --context SQLiteContext
|
||||
```
|
||||
:::
|
||||
|
||||
### Création des tables
|
||||
|
||||
Tapez ensuite les commandes suivantes :
|
||||
|
||||
```
|
||||
dotnet ef database update --context SqlServerContext
|
||||
dotnet ef database update --context SQLiteContext
|
||||
```
|
||||
|
||||
:::info
|
||||
sous MacOSX, n'utilisez que SQLite, soit :*
|
||||
|
||||
```
|
||||
dotnet ef database update --context SQLiteContext
|
||||
```
|
||||
:::
|
||||
|
||||
### Génération et exécution
|
||||
|
||||
Vous pouvez maintenant générer et exécuter l'exemple.
|
||||
|
||||
Le résultat de l'exécution peut donner :
|
||||
|
||||
* sur Windows :
|
||||
|
||||
```
|
||||
Creates and inserts new Nounours with SqlServer
|
||||
1 - Chewbacca
|
||||
2 - Yoda
|
||||
3 - Ewok
|
||||
Creates and inserts new Nounours with SQLite
|
||||
1 - Chewbacca
|
||||
2 - Yoda
|
||||
3 - Ewok
|
||||
```
|
||||
|
||||
* sur MacOSX :
|
||||
|
||||
```
|
||||
Creates and inserts new Nounours with SQLite
|
||||
1 - Chewbacca
|
||||
2 - Yoda
|
||||
3 - Ewok
|
||||
```
|
||||
|
||||
## Comment vérifier le contenu des bases de données SQL Server et SQLite ?
|
||||
|
||||
### SqlServer (seulement sur Windows)
|
||||
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *myFirstDatabase.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|Id |Nom
|
||||
|---|---
|
||||
|1|Chewbacca
|
||||
|2|Yoda
|
||||
|3|Ewok
|
||||
|
||||
Notez qu'il est également possible d'utiliser l'*Explorateur d'objets SQL Server* pour ajouter, modifier ou supprimer des données dans les tables.
|
||||
|
||||
### SQLite (Windows et MacOSX)
|
||||
|
||||
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_041_001_ConnectionStrings.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_041_001_ConnectionStrings.csproj*.
|
||||

|
||||
* Choisissez l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus
|
||||

|
||||
|
||||
---
|
||||
Copyright © 2019-2020 Marc Chevaldonné
|
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 78 KiB |
@ -1,573 +0,0 @@
|
||||
---
|
||||
sidebar_label: '1.2. Testing In Memory'
|
||||
sidebar_position: 2
|
||||
description: "présente comment utiliser des fournisseurs en mémoire pour éviter la surchage de la création d'une base de données en particulier dans le cas de tests unitaires. Cet exemple est composé de 4 projets."
|
||||
---
|
||||
|
||||
# ex_041_004_TestingInMemory
|
||||
*02/01/2020 ⋅ Marc Chevaldonné*
|
||||
*Dernière mise à jour : 09/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_041_004_TestingInMemory)
|
||||
|
||||
---
|
||||
|
||||
Lorsqu'on cherche à tester notre code et nos accès à la base de données, on n'a pas nécessairement envie de créer la base juste pour les tests.
|
||||
Pour cela, il existe des solutions et des fournisseurs permettant de tester les bases sans avoir à réellement les créer :
|
||||
* le fournisseur **InMemory** permet ceci mais de manière approximative, car **InMemory** n'est pas une base de données relationnelle : il y a donc des limitations.
|
||||
* **SQLite** possède un mode *In-Memory* qui lui, permet de tester une base de données relationnelle, sans avoir à créer une base de données.
|
||||
|
||||
:::tip
|
||||
**Je conseille donc l'utilisation de _SQL in Memory_ plutôt que InMemory, puisqu'il permet de tester une base relationnelle.**
|
||||
:::
|
||||
|
||||
Cet exemple montre comment utiliser **InMemory** et **SQLite in-memory** à travers une injection de dépendance. En d'autres termes, vous continuez à définir votre chaîne de connexion sur une base de données, mais vous permettez néanmoins l'utilisation, à la demande, de **InMemory** pour des tests.
|
||||
Puisque ce fournisseur devient intéressant dans le cas de tests, j'ai donc ajouté un 2ème projet lié à cet exemple, permettant d'avoir accès à des tests unitaires utilisant **InMemory** ou **SQLite in-memory**.
|
||||
Pour le reste de l'exemple, celui-ci n'apporte rien de nouveau par rapport à l'exemple ex_041_001 concernant l'utilisation d'**Entity Framework Core**.
|
||||
|
||||
---
|
||||
|
||||
## Pourquoi autant de projets dans cet exemple ?
|
||||
|
||||
Quatre projets constituent cet exemple :
|
||||
* **ex_041_004_TestingInMemory** est une bibliothèque de classes .NET Standard contentant le modèle, ie la classe ```Nounours``` et la classe ```NounoursContext```
|
||||
* **ex_041_004_ConsoleTests_w_SqlServer** est une application console .NET Core qui prouve le fonctionnement "normal" de la base de données (ie comme dans l'exemple ex_041_001) en exploitant la bibliothèque de classes **ex_041_004_TestingInMemory** (ne fonctionne que sur Windows)
|
||||
* **ex_041_004_ConsoleTests_w_SQLite** est une application console .NET Core qui prouve le fonctionnement "normal" de la base de données (ie comme dans l'exemple ex_041_001) en exploitant la bibliothèque de classes **ex_041_004_TestingInMemory** (cross-platform)
|
||||
* **ex_041_004_UnitTests_w_InMemory** est une application de tests unitaires xUnit exploitant le fournisseur **InMemory** et la bibliothèque de classes **ex_041_004_TestingInMemory**
|
||||
* **ex_041_004_UnitTests_w_SQLiteInMemory** est une application de tests unitaires xUnit exploitant le fournisseur **SQLite in memory** et la bibliothèque de classes **ex_041_004_TestingInMemory**
|
||||
|
||||
Vous pouvez donc exécuter cet exemple de quatre manières :
|
||||
* via **ex_041_004_ConsoleTests_w_SqlServer** comme dans l'exemple ex_041_001 avec ```dotnet ef``` (seulement sur Windows)
|
||||
* via **ex_041_004_ConsoleTests_w_SQLite** comme dans l'exemple ex_041_001 avec ```dotnet ef```
|
||||
* via les tests unitaires de **ex_041_004_UnitTests_w_InMemory**
|
||||
* via les tests unitaires de **ex_041_004_UnitTests_w_SQLiteInMemory**
|
||||
|
||||
## Comment a été construit cet exemple ?
|
||||
|
||||
### bibliothèque .NET Standard ex_041_004_TestingInMemory
|
||||
Cet exemple est tout d'abord construit de la même manière que l'exemple *ex_041_001_ConnectionStrings*.
|
||||
Il ne faut pas oublier les NuGet nécessaires :
|
||||
* Microsoft.EntityFrameworkCore : pour le projet en général
|
||||
* Microsoft.EntityFrameworkCore.SqlServer : pour le *provider* SQL Server
|
||||
* Microsoft.EntityFrameworkCore.Tools : pour bénéficier des outils de Design, de migrations, etc.
|
||||
|
||||
J'ai ensuite décidé de renommer ma classe dérivant de ```DbContext``` en ```NounoursContext``` car je n'ai plus de raison de faire la différence entre SqlServer et SQLite.
|
||||
On obtient ainsi la classe ```NounoursContext``` suivante :
|
||||
|
||||
```cs title="NounoursContext.cs"
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ex_041_004_TestingInMemory
|
||||
{
|
||||
public class NounoursContext : DbContext
|
||||
{
|
||||
public DbSet<Nounours> Nounours { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_TestingInMemory.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On la modifie pour permettre d'injecter un autre fournisseur, tout en gardant celui-ci par défaut. On peut utiliser pour cela la propriété ```IsConfigured``` sur ```DbContextOptionsBuilder```.
|
||||
La méthode ```OnConfiguring``` de ```NounoursContext``` est alors modifiée de la manière suivante :
|
||||
```csharp title="NounoursContext.cs"
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
if (!options.IsConfigured)
|
||||
{
|
||||
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_TestingInMemory.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dès lors, si rien n'est configuré, c'est donc le fournisseur *SqlServer* qui sera utilisé.
|
||||
Mais nous pouvons aussi permettre à l'utilisateur d'en injecter un autre. Pour cela, nous ajoutons deux constructeurs à notre classe :
|
||||
* ```NounoursContext()``` : le constructeur par défaut ne fait rien, et en conséquence fera que la configuration *SqlServer* sera utilisée,
|
||||
* ```NounoursContext(DbContextOptions<NounoursContext> options)``` : ce constructeur par défaut permettra d'injecter une autre fabrique de fournisseur, et permettra à l'utilisateur d'injecter n'importe quel autre fournisseur, dont **InMemory**.
|
||||
|
||||
Les constructeurs injectés sont donc :
|
||||
```csharp title="NounoursContext.cs"
|
||||
public NounoursContext()
|
||||
{ }
|
||||
|
||||
public NounoursContext(DbContextOptions<NounoursContext> options)
|
||||
: base(options)
|
||||
{ }
|
||||
```
|
||||
La classe ```NounoursContext``` peut donc s'utiliser comme précédemment, sans changement, et la base *SqlServer* sera utilisée ; ou alors, on pourra injecter un autre fournisseur.
|
||||
|
||||
### application console .NET Core ex_041_004_ConsoleTests_w_SqlServer (seulement pour Windows)
|
||||
|
||||
L'application console .NET Core **ex_041_004_ConsoleTests_w_SqlServer** fait référence à la bibliothèque .NET Standard précédente pour pouvoir consommer ```Nounours``` et ```NounoursContext```.
|
||||
Sa seule classe est donc ```Program``` et peut donc être codée de la manière suivante :
|
||||
|
||||
```csharp title="Program.cs"
|
||||
using System;
|
||||
using ex_041_004_TestingInMemory;
|
||||
|
||||
namespace ex_041_004_ConsoleTests_w_SqlServer
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
using (var context = new NounoursContext())
|
||||
{
|
||||
// Crée des nounours et les insère dans la base
|
||||
Console.WriteLine("Creates and inserts new Nounours");
|
||||
context.Add(chewie);
|
||||
context.Add(yoda);
|
||||
context.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NounoursContext())
|
||||
{
|
||||
foreach(var n in context.Nounours)
|
||||
{
|
||||
Console.WriteLine($"{n.Id} - {n.Nom}");
|
||||
}
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_041_004_ConsoleTests_w_SqlServer
|
||||
```
|
||||
:::info
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration : comme la classe dérivant de ```DbContext``` n'est pas dans l'application Console, nous devons préciser dans quel projet elle se trouve en ajoutant ```--project ../ex_041_004_TestingInMemory```.
|
||||
```
|
||||
dotnet ef migrations add migration_ex_041_004 --project ../ex_041_004_TestingInMemory
|
||||
```
|
||||
* Création de la table : comme pour la migration, il faut préciser dans quel projet se trouve l'instance de ```DbContext```.
|
||||
```
|
||||
dotnet ef database update --project ../ex_041_004_TestingInMemory
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_041_004_ConsoleTests_w_SqlServer**.
|
||||
|
||||
* Le résultat de l'exécution doit ressembler à :
|
||||
```
|
||||
Creates and inserts new Nounours
|
||||
1 - Chewbacca
|
||||
2 - Yoda
|
||||
3 - Ewok
|
||||
```
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_041_004_TestingInMemory.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|Id |Nom
|
||||
|---|---
|
||||
|1|Chewbacca
|
||||
|2|Yoda
|
||||
|3|Ewok
|
||||
|
||||
### application console .NET Core ex_041_004_ConsoleTests_w_SQLite
|
||||
|
||||
L'application console .NET Core **ex_041_004_ConsoleTests_w_SQLite** fait référence à la bibliothèque .NET Standard précédente pour pouvoir consommer ```Nounours``` et ```SQLiteNounoursContext```.
|
||||
Ses deux seules classes sont donc ```Program``` et ```SQLiteNounoursContext``` et sont codées de la manière suivante :
|
||||
```csharp title="Program.cs"
|
||||
using System;
|
||||
using ex_041_004_TestingInMemory;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ex_041_004_ConsoleTests_w_SQLite
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
using (var context = new SQLiteNounoursContext())
|
||||
{
|
||||
// Crée des nounours et les insère dans la base
|
||||
Console.WriteLine("Creates and inserts new Nounours");
|
||||
context.Add(chewie);
|
||||
context.Add(yoda);
|
||||
context.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SQLiteNounoursContext : NounoursContext
|
||||
{
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
if(!options.IsConfigured)
|
||||
{
|
||||
options.UseSqlite($"Data Source=ex_041_004_SQLite.Nounours.db");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
La classe ```SQLiteNounoursContext``` a pour but de permettre l'appel de ```dotnet ef``` sans avoir à utiliser le ```NounoursContext``` qui utilise SqlServer. En effet, pour pouvoir
|
||||
mettre à jour la base SQLite, EFCore demande pour le moment, un ```DbContext``` correspondant à la base à mettre à jour dans la méthode ```OnConfiguring```.
|
||||
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
|
||||
Ou bien ouvrez le terminal (sous MacOSX)
|
||||
* Dans la console ou le terminal que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
|
||||
```
|
||||
cd .\p08_BDD_EntityFramework\ex_041_004_ConsoleTests_w_SQLite
|
||||
```
|
||||
|
||||
:::info
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration : comme la classe dérivant de ```DbContext``` se trouve dans l'application Console, nous n'avons pas à préciser dans quel projet elle se trouve. En revanche, il y a désormais deux contextes (celui d'origine ```NounoursContext``` et celui pour SQLite ```SQLiteNounoursContext```), il faut donc préciser le contexte avec ```--context``` :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_041_004 --context SQLiteNounoursContext
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update --context SQLiteNounoursContext
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_041_004_ConsoleTests_w_SQLite**.
|
||||
|
||||
* Le résultat de l'exécution doit ressembler à :
|
||||
|
||||
```
|
||||
Creates and inserts new Nounours
|
||||
1 - Chewbacca
|
||||
2 - Yoda
|
||||
3 - Ewok
|
||||
```
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQLite ?
|
||||
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_041_004_ConsoleTests_w_SQLite.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_041_004_ConsoleTests_w_SQLite.csproj*.
|
||||

|
||||
* Choisissez l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus
|
||||

|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|Id |Nom
|
||||
|---|---
|
||||
|1|Chewbacca
|
||||
|2|Yoda
|
||||
|3|Ewok
|
||||
|
||||
### Configuration des tests unitaires avec InMemory
|
||||
Le test unitaire est de type *xUnit* et va permettre d'injecter le fournisseur **InMemory**.
|
||||
* On crée un nouveau projet de tests unitaires (*xUnit*)
|
||||
* On lui ajoute le package NuGet : *Microsoft.EntityFrameworkCore.InMemory*
|
||||
* On ajoute également une référence au projet précédent (*ex_041_004_InMemory.exe*)
|
||||
* On peut ensuite écrire un premier test comme suit :
|
||||
```csharp title="NounoursDB_Tests"
|
||||
using ex_041_004_InMemory;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace ex_041_004_UnitTests
|
||||
{
|
||||
public class NounoursDB_Tests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_Test()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<NounoursContext>()
|
||||
.UseInMemoryDatabase(databaseName: "Add_Test_database")
|
||||
.Options;
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
context.Nounours.Add(chewie);
|
||||
context.Nounours.Add(yoda);
|
||||
context.Nounours.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
Assert.Equal(3, context.Nounours.Count());
|
||||
Assert.Equal("Chewbacca", context.Nounours.First().Nom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ce premier test permet d'ajouter 3 nounours et :
|
||||
* de vérifier qu'il y a bien trois nounours ajoutés
|
||||
```csharp
|
||||
Assert.Equal(3, context.Nounours.Count());
|
||||
```
|
||||
* de vérifier que le premier s'appelle bien Chewbacca :
|
||||
```csharp
|
||||
Assert.Equal("Chewbacca", context.Nounours.First().Nom);
|
||||
```
|
||||
|
||||
Notez que le choix du fournisseur est bien fait au démarrage du test avec la création du ```DbContextOptionsBuilder``` :
|
||||
```csharp
|
||||
var options = new DbContextOptionsBuilder<NounoursContext>()
|
||||
.UseInMemoryDatabase(databaseName: "Add_Test_database")
|
||||
.Options;
|
||||
```
|
||||
et que l'injection est effectuée plus bas :
|
||||
```csharp
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
On peut ensuite ajouter un autre test, par exemple :
|
||||
```csharp title="NounoursDB_Tests"
|
||||
[Fact]
|
||||
public void Modify_Test()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<NounoursContext>()
|
||||
.UseInMemoryDatabase(databaseName: "Modify_Test_database")
|
||||
.Options;
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
context.Nounours.Add(chewie);
|
||||
context.Nounours.Add(yoda);
|
||||
context.Nounours.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
nameToFind = "ewo";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
|
||||
ewok.Nom = "Wicket";
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
nameToFind = "wick";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
}
|
||||
}
|
||||
```
|
||||
Ce cas de test :
|
||||
* vérifie d'abord qu'il y a deux nounours dont le nom contient la chaîne "ew",
|
||||
```csharp
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
```
|
||||
* vérifie qu'il y a un nounours dont le nom contient la chaîne "ewo"
|
||||
```csharp
|
||||
nameToFind = "ewo";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
```
|
||||
* modifie le nom de ce nounours en "Wicket"
|
||||
```csharp
|
||||
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
|
||||
ewok.Nom = "Wicket";
|
||||
```
|
||||
* enregistre les changements
|
||||
```csharp
|
||||
context.SaveChanges();
|
||||
```
|
||||
* vérifie ensuite qu'il n'y a plus qu'un Nounours dont le nom contient la chaîne "ew"
|
||||
```csharp
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
```
|
||||
* vérifie qu'il y a un Nounours dont le nom contient la chaîne "wick"
|
||||
```csharp
|
||||
nameToFind = "wick";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
```
|
||||
|
||||
### Configuration des tests unitaires avec SQLite in memory
|
||||
Le projet se construit exactement da la même manière que le précédent à quelques exceptions près que voici.
|
||||
* package NuGet :
|
||||
à la place du NuGet *Microsoft.EntityFrameworkCore.InMemory*, il faut ajouter *Microsoft.EntityFrameworkCore.Sqlite*
|
||||
* ouverture de la connexion :
|
||||
au début des tests, il faut penser à ouvrir la connexion avec la base en mémoire SQLite qui doit rester ouverte durant tout le test.
|
||||
```csharp
|
||||
//connection must be opened to use In-memory database
|
||||
var connection = new SqliteConnection("DataSource=:memory:");
|
||||
connection.Open();
|
||||
```
|
||||
* avant de commencer à traiter avec la base en mémoire, on peut vérifier qu'elle a bien été créée :
|
||||
```csharp
|
||||
//context.Database.OpenConnection();
|
||||
context.Database.EnsureCreated();
|
||||
```
|
||||
Au final, les tests ressemblent à :
|
||||
```csharp title="NounoursDB_Tests"
|
||||
using ex_041_004_TestingInMemory;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
|
||||
namespace ex_041_004_UnitTests_w_SQLiteInMemory
|
||||
{
|
||||
public class NounoursDB_Tests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_Test()
|
||||
{
|
||||
//connection must be opened to use In-memory database
|
||||
var connection = new SqliteConnection("DataSource=:memory:");
|
||||
connection.Open();
|
||||
|
||||
var options = new DbContextOptionsBuilder<NounoursContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
//context.Database.OpenConnection();
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
context.Nounours.Add(chewie);
|
||||
context.Nounours.Add(yoda);
|
||||
context.Nounours.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//uses another instance of the context to do the tests
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
Assert.Equal(3, context.Nounours.Count());
|
||||
Assert.Equal("Chewbacca", context.Nounours.First().Nom);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Modify_Test()
|
||||
{
|
||||
//connection must be opened to use In-memory database
|
||||
var connection = new SqliteConnection("DataSource=:memory:");
|
||||
connection.Open();
|
||||
|
||||
var options = new DbContextOptionsBuilder<NounoursContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
|
||||
//prepares the database with one instance of the context
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
//context.Database.OpenConnection();
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca" };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda" };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok" };
|
||||
|
||||
context.Nounours.Add(chewie);
|
||||
context.Nounours.Add(yoda);
|
||||
context.Nounours.Add(ewok);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//uses another instance of the context to do the tests
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
nameToFind = "wo";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
|
||||
ewok.Nom = "Wicket";
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
//uses another instance of the context to do the tests
|
||||
using (var context = new NounoursContext(options))
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
string nameToFind = "ew";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
nameToFind = "wick";
|
||||
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### exécution des tests unitaires
|
||||
Vous pouvez maintenant exécuter les tests unitaires via l'*Eplorateur de tests*.
|
||||
* Dans le menu *Test*, choisissez *Explorateur de tests*
|
||||

|
||||
* Cliquez sur "Exécuter tous les tests"
|
||||

|
||||
* Observez le bon fonctionnement
|
||||
|
||||
|
||||
---
|
||||
Copyright © 2019-2020 Marc Chevaldonné
|
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 100 KiB |
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "1. Fundamentals",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Dans cette partie, je donnerai quelques notions pour se connecter à une base à l'aide de chaîne de connection (connection strings), comment utiliser des providers de tests.... Il s'agira en conséquence d'exemples simples manquants d'explications sur certains points, car ils seront présentés plus tard."
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
---
|
||||
sidebar_label: '6.1. MAUI, EF et SQLite'
|
||||
sidebar_position: 1
|
||||
description: "explique comment utiliser le fournisseur SQLite et Entity Framework dans une application MAUI"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# Utilisation du fournisseur SQLite, via Entity Framework, dans une application MAUI
|
||||
*23/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::info Pré-requis
|
||||
Cette page n'explique pas comment écrire un ```DbContext``` ou comment utiliser *Entity Framework*.
|
||||
Référez-vous aux articles précédents pour récupérer ces informations.
|
||||
:::
|
||||
|
||||
## Structure
|
||||
|
||||
Considérons une application **MAUI_App** consommant une bibliothèque de classe **EFLib**
|
||||
contenant une classe fille de ```DbContext```, appelée ```MyDbContext```.
|
||||
|
||||
<Mermaid chart={`
|
||||
flowchart LR
|
||||
MAUI_App -.-> EFLib;
|
||||
`}/>
|
||||
|
||||
```MyDbContext``` possède un constructeur permettant l'injection d'un ```DbContextOptions<MyDbContext>``` afin de proposer le fournisseur de son choix et la chaîne de connexion adaptée.
|
||||
|
||||
```csharp title='MyDbContext.cs'
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
public MyDbContext() { }
|
||||
|
||||
public MyDbContext(DbContextOptions<MyDbContext> options)
|
||||
: base(options)
|
||||
{ }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
//...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Injection d'un ```DbContext``` dans l'application MAUI
|
||||
|
||||
Pour injecter un ```DbContext```, dans la classe ```MauiProgram``` (ou son équivalent), on peut utiliser la méthode d'extension ```AddDbContext```.
|
||||
|
||||
```csharp title='MauiProgram.cs'
|
||||
public static class MauiProgram
|
||||
{
|
||||
const string DatabaseFilename = "MyDatabase.db";
|
||||
|
||||
public static MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
|
||||
//...
|
||||
|
||||
builder.Services.AddDbContext<MyDbContext>(opt => opt.UseSqlite($"Filename={Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename)}"));
|
||||
|
||||
//...
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```AddDbContext``` prend en paramètre une ```Action<DbContextOptionsBuilder>``` vous permettant notamment de passer un ```DbContextOptionsBuilder``` construit à la volée.
|
||||
|
||||
```FileSystem.AppDataDirectory``` permet d'atteindre le dossier spécial de données de l'application, quel que soit l'OS.
|
||||
C'est le dossier recommandé pour placer votre base de données, mais vous pouvez choisir un autre emplacement.
|
||||
|
||||
## Consommation dans une page, une VM, ou un autre service
|
||||
Vous pouvez désormais bénéficier de l'injection de dépendances de MAUI pour utiliser votre DbContext, dans une page, dans une VM ou dans un autre service.
|
||||
|
||||
Si on veut par exemple l'utiliser dans la page ```MainPage```, il suffit d'ajouter un membre de type ```MyDbContext``` à ```MainPage```, et de l'injecter via le constructeur.
|
||||
```csharp title='MainPage.xaml.cs'
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
private ArtistsDbContext context;
|
||||
|
||||
public MainPage(ArtistsDbContext context)
|
||||
{
|
||||
//...
|
||||
this.context = context;
|
||||
context.Database.EnsureCreated();
|
||||
InitializeComponent();
|
||||
//...
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
:::caution context.Database.EnsureCreated();
|
||||
Notez l'appel de ```context.Database.EnsureCreated();``` qui permet de garantir l'existence de la base de données avant son utilisation.
|
||||
Si elle n'existe pas, elle est créée lors de l'appel de cette méthode.
|
||||
:::
|
||||
|
||||
:::tip EnsuredCreated() dans la classe ```DbContext```
|
||||
Vous pouvez aussi appeler ```Database.EnsureCreated();``` directement dans le code de votre classe dérivant de ```DbContext``` pour
|
||||
vous éviter d'avoir à le faire dans vos vues ou vos VMs, si vous avez accès à ce code.
|
||||
:::
|
||||
|
||||
On peut ensuite déclarer la page et réaliser l'injection du ```DbContext``` dans la classe ```MauiProgram``` (ou son équivalent) :
|
||||
```csharp title='MauiProgram.cs'
|
||||
builder.Services.AddDbContext<MyDbContext>(opt => opt.UseSqlite($"Filename={Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename)}"))
|
||||
.AddSingleton<MainPage>();
|
||||
```
|
||||
|
||||
On peut aussi récupérer le service à l'aide d'un ```ServiceProvider``` injecté. Par exemple, dans ```MainPage``` (mais réalisable n'importe où) :
|
||||
```csharp title='MainPage.xaml.cs'
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public MainPage(IServiceProvider service)
|
||||
{
|
||||
serviceProvider = service;
|
||||
var ctxt = serviceProvider.GetRequiredService<ArtistsDbContext>();
|
||||
ctxt.Database.EnsureCreated();
|
||||
InitializeComponent();
|
||||
|
||||
//...
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
À vous d'adapter ce code pour injecter votre ```DbContext``` dans une VM ou un service.
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "6. MAUI & Entity Framework",
|
||||
"position": 6,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Comment utiliser une base de données via Entity Framework dans une application MAUI ?"
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.9. Data Seeding'
|
||||
sidebar_position: 9
|
||||
description: "explique comment utiliser un stub (méthode recommandée depuis EF Core 2.1)"
|
||||
---
|
||||
|
||||
# Data Seeding
|
||||
*14/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_009_DataSeeding)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment il est recommandé d'utiliser du Stub avec Entity Framework Core depuis la version 2.1.
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient trois classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
* ```NounoursDBEntitiesWithStub```
|
||||
|
||||
Le contenu des classes ```Nounours``` et ```NounoursDBEntities``` correspond à ce qui a été vu dans les exemples précédents. Seule la classe ```NounoursDBEntitiesWithStub``` sera donc expliquée ici.
|
||||
|
||||
### La classe ```NounoursDBEntitiesWithStub```
|
||||
|
||||
* ```NounoursDBEntitiesWithStub``` est une classe fille de ```NounoursDBEntites```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
|
||||
namespace ex_042_009_DataSeeding
|
||||
{
|
||||
class NounoursDBEntitiesWithStub : NounoursDBEntities
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new Nounours { UniqueId = Guid.Parse("{4422C524-B2CB-43EF-8263-990C3CEA7CAE}"), Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 },
|
||||
new Nounours { UniqueId = Guid.Parse("{A4F84D92-C20F-4F2D-B3F9-CA00EF556E72}"), Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 },
|
||||
new Nounours { UniqueId = Guid.Parse("{AE5FE535-F041-445E-B570-28B75BC78CB9}"), Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
:::caution Note importante
|
||||
Remarquez que la création des instances d'entités donne aussi l'```UniqueId``` puisqu'il ne s'agit pas d'un ajout "classique" dans la base mais la table est créée avec ces instances.
|
||||
:::
|
||||
:::caution Note importante
|
||||
L'utilisation de ```UniqueId``` vous permettra d'ajouter des entités liées dans le Stub.
|
||||
:::
|
||||
:::tip Explication
|
||||
L'utilisation de ```HasData``` dans ```OnModelCreating``` fait que vos données stubbées feront parties de la migration : rien à voir avec un ajout depuis votre appli consommatrice.
|
||||
:::
|
||||
:::tip Note
|
||||
Souvent, on en profite pour réécrire également ```OnConfiguring``` afin de changer de fournisseur (pour prendre par exemple un *SQLite in memory* puisque ce contexte stubbé est voué à être utilisé par des tests).
|
||||
:::
|
||||
|
||||
* Elle est ensuite utilisée dans ```Program``` pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntitiesWithStub())
|
||||
{
|
||||
WriteLine("Contenu de la base :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_009_DataSeeding
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_009 --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_009_DataSeeding**.
|
||||
|
||||
* Le résultat de l'exécution va ressembler à :
|
||||
```
|
||||
Contenu de la base :
|
||||
ae5fe535-f041-445e-b570-28b75bc78cb9: Ewok (25/05/1983, 3456789 poils)
|
||||
4422c524-b2cb-43ef-8263-990c3cea7cae: Chewbacca (27/05/1977, 1234567 poils)
|
||||
a4f84d92-c20f-4f2d-b3f9-ca00ef556e72: Yoda (21/05/1980, 3 poils)
|
||||
```
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_009_DataSeeding.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|UniqueId |Nom|Naissance|NbPoils
|
||||
|---|---|---|---
|
||||
|ae5fe535-f041-445e-b570-28b75bc78cb9|Ewok|25/05/1983|3456789
|
||||
|4422c524-b2cb-43ef-8263-990c3cea7cae|Chewbacca|27/05/1977|1234567
|
||||
|a4f84d92-c20f-4f2d-b3f9-ca00ef556e72|Yoda|21/05/1980|3
|
||||
|
@ -1,131 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.8. Data Seeding Before Entity Framework 2.1'
|
||||
sidebar_position: 8
|
||||
description: "explique comment utiliser un stub (méthode qui était recommandée avant EF Core 2.1)"
|
||||
---
|
||||
|
||||
# Data Seeding Before Entity Framework 2.1
|
||||
*13/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_008_DataSeeding_before_EF2.1)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment il était recommandé d'utiliser du Stub avec Entity Framework Core avant la version 2.1.
|
||||
:::caution Conseil
|
||||
Il est conseillé d'utiliser une méthode plus moderne (cf. [**2.9. Data Seeding**](/docs/Entity-Framework/Model/DataSeeding)).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient trois classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
* ```DataSeeder```
|
||||
|
||||
Le contenu des classes ```Nounours``` et ```NounoursDBEntities``` correspond à ce qui a été vu dans les exemples précédents. Seule la classe ```DataSeeder``` sera donc expliquée ici.
|
||||
|
||||
### La classe ```DataSeeder```
|
||||
|
||||
* ```DataSeeder``` est une classe statique possédant une méthode ```SeedData``` dont le but est d'ajouter des éléments à la base.
|
||||
```csharp title='DataSeeder.cs'
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
|
||||
namespace ex_042_008_DataSeeding_before_EF2_1
|
||||
{
|
||||
public static class DataSeeder
|
||||
{
|
||||
public static void SeedData(DbContext context)
|
||||
{
|
||||
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 };
|
||||
|
||||
context.AddRange(new Nounours[] { chewie, yoda, ewok });
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle est utilisée dans ```Program``` pour remplir la base.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
DataSeeder.SeedData(db);
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_008_DataSeeding_before_EF2_1
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_008
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_008_DataSeeding_before_EF2.1**.
|
||||
|
||||
* Le résultat de l'exécution peut ressembler à :
|
||||
```
|
||||
nettoyage de la base car elle n'était pas vide
|
||||
remplissage avec du stub
|
||||
Contenu de la base :
|
||||
9540729e-67d9-442c-63fb-08d798777717: Chewbacca (27/05/1977, 1234567 poils)
|
||||
df1ce76a-97ee-42c7-63fc-08d798777717: Yoda (21/05/1980, 3 poils)
|
||||
0b921251-12a6-480a-63fd-08d798777717: Ewok (25/05/1983, 3456789 poils)
|
||||
```
|
||||
:::note
|
||||
Les identifiants seront bien sûr différents
|
||||
:::
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_008_DataSeeding_before_EF2_1.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|UniqueId |Nom|Naissance
|
||||
|---|---|---
|
||||
|9540729e-67d9-442c-63fb-08d798777717|Chewbacca|27/05/1977
|
||||
|df1ce76a-97ee-42c7-63fc-08d798777717|Yoda|21/05/1980
|
||||
|0b921251-12a6-480a-63fd-08d798777717|Ewok|25/05/1983
|
||||
:::note
|
||||
les identifiants seront bien sûr différents.
|
||||
:::
|
||||
:::note
|
||||
Notez l'absence de la colonne "NbPoils"
|
||||
:::
|
||||
:::note
|
||||
Notez le nom de la colonne "Naissance" et le formatage de la date
|
||||
:::
|
@ -1,323 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.3. Fluent API (Entity Framework Code First)'
|
||||
sidebar_position: 3
|
||||
description: "explique comment utiliser la Fluent API pour personnaliser la transformation d'une entité en table"
|
||||
---
|
||||
|
||||
# Fluent API (Entity Framework Code First)
|
||||
*03/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API)
|
||||
|
||||
---
|
||||
|
||||
Le lien entre une base de données et vos classes à l'aide de **Entity Framework Core** se fait via une classe dérivant de ```DbContext```.
|
||||
Cette classe doit contenir des ```DbSet<T>```. Chaque ```DbSet<T>``` correspond à une table et ```T``` correspond à une de vos classes qu'on appelle *entité*.
|
||||
Le lien entre les tables et les entités est géré plus ou moins automatiquement par le *framework*.
|
||||
**EF Core** permet de lire/écrire les instances d'entité de la base de données ; permet de créer des tables pour les entités via les migrations (pour les bases de données relationnelles) ;
|
||||
les types exposés dans les propriétés d'une entité deviennent automatiquement des entités (mais pas forcément des tables)
|
||||
|
||||
**Entity Framework Core** propose 3 solutions différentes pour relier des entités à des tables de la base de données :
|
||||
* **conventions d'écriture** : c'est la solution la plus simple, qui analyse le code de votre classe pour en déduire de quelle manière la relier à la table.
|
||||
* **data annotations** : elle se base sur des décorateurs (appelés *data annotations*) que vous placez autour de vos propriétés pour indiquer de quelle manière les relier aux colonnes de votre table. Les *data annotations* écrasent les conventions d'écriture.
|
||||
* **Fluent API** : directement dans le code de votre ```DbContext``` (plus précisément, dans la méthode ```OnModelCreating```), vous précisez comment se fait le *mapping* entre votre entité et votre table. La *Fluent API* écrase les conventions d'écriture et les *data annotations*.
|
||||
|
||||
En d'autres termes, si vous n'écrivez rien, **EF Core** utilise les conventions d'écriture ; si vous n'utilisez que des *data annotations*, elles sont prioritaires sur les convetions d'écriture ; si vous utilisez la *Fluent API*, elle est prioritaire sur les deux autres méthodes.
|
||||
Mais on peut faire un mix des différentes méthodes.
|
||||
|
||||
Cet exemple montre de quelle manière la **Fluent API** est utilisée pour transformer une entité en table.
|
||||
Il montre notamment :
|
||||
* comment choisir le nom de la table
|
||||
* comment ignorer une propriété de l'entité
|
||||
* comment le nom et le type d'une colonne de la table peuvent être modifiés
|
||||
* comment un identifiant unique est choisi et généré.
|
||||
|
||||
Cette méthode est préférable dans les deux cas principaux suivants (et en particulier le 1er) :
|
||||
* nous n'avons pas accès au code source de la classe Nounours ou bien nous n'avons pas le droit de le modifier, et les conventions d'écriture ne
|
||||
correspondent pas à ce que nous souhaitons réaliser.
|
||||
* nous ne souhaitons pas "polluer" la classe POCO avec des annotations de données.
|
||||
L'inconvénient majeur et évident de cette méthode est que la lecture et la maintenance sont plus compliquées.
|
||||
|
||||
Cet exemple est répété d'une manière très similaire en utilisant les autres méthodes de *mapping* entre entité et table :
|
||||
* [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions)
|
||||
* [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient deux classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
|
||||
### La classe ```Nounours```
|
||||
* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object.
|
||||
```csharp title='Nounours.cs'
|
||||
public class Nounours
|
||||
{
|
||||
public Guid UniqueId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle contient 4 propriétés en lecture/écriture : ```UniqueId```, ```Nom```, ```DateDeNaissance``` et ```NbPoils```.
|
||||
* **Entity Framework Core** va utiliser la classe POCO ```Nounours``` pour créer une table dans la base de données, lorsque le ```DbSet``` va être créé.
|
||||
* L'utilisation des conventions d'écriture d'Entity Framework pourrait s'appliquer car il n'y a pas de *data annotations* (cf. ex_042_002) ; mais comme la classe ```NounoursDbEntities``` utilise la *Fluent API*, elles seront écrasées.
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
* Cette classe dérive de ```DbContext```. Elle contient des ```DbSet<T>``` où ```T``` est une entité. Elle contient autant de ```DbSet<T>``` que de tables.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
class NounoursDBEntities : DbContext
|
||||
{
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
}
|
||||
```
|
||||
ici, on indique donc qu'il y aura une table de ```Nounours```.
|
||||
* **EF Core** utilisera cette classe pour faire la création de la base de données et de ses tables. Pour cela, elle utilise la chaîne de connexion donnée dans la méthode ```OnConfiguring```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_003_EF_CF_Fluent_API.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
```
|
||||
=> cf **ex_041_001_ConnectionStrings** pour en savoir plus sur les chaîne de connexion
|
||||
* Dans cet exemple, la table de ```Nounours``` est créée en utilisant la *Fluent API* et les conventions d'écriture de la classe ```Nounours```.
|
||||
|
||||
* La **Fluent API** est utilisée à travers la méthode ```OnModelCreating```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
|
||||
.HasMaxLength(256);
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date");
|
||||
|
||||
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
```
|
||||
L'instance de ```ModelBuilder``` permet ensuite d'atteindre les entités avec la méthode générique ```Entity<T>()```où ```T```est l'entité.
|
||||
A partir d'une entité, on peut ensuite atteindre une propriété à l'aide de la méthode ```Property(...)``` pouvant prendre en paramètre
|
||||
soit le nom de la propriété, soit une expression lambda (recommandé pour éviter les erreurs de compilation).
|
||||
Ensuite, à l'aide des méthodes d'extension, on peut définir des contraintes sur les entités et leurs propriétés.
|
||||
|
||||
|
||||
|
||||
Si on prend chaque ligne de la méthode ```OnModelCreating``` en détail, on peut voir :
|
||||
* le choix du nom de la table associée à l'entité ```Nounours```
|
||||
*équivalent du ```[Table("TableNounours")]``` avec les annotations de données*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
|
||||
```
|
||||
* on peut changer le nom et le type d'une colonne avec les méthodes d'extension ```HasColumnName``` et ```HasColumnType``` sur la propriété de l'entité.
|
||||
Dans l'exemple ci-dessous, la colonne ne s'appellera pas "DateDeNaissance" (comme le permettraient les conventions d'écriture), mais "Naissance" grâce à la *Fluent API* et son type sera ```date``` au lieu de ```datetime2(7)``` avec les conventions d'écriture.
|
||||
*équivalent avec une annotation de ```[Column("Naissance", TypeName="date")]``` devant la propriété ```DateDeNaissance``` de ```Nounours```*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance)
|
||||
.HasColumnName("Naissance")
|
||||
.HasColumnType("date");
|
||||
```
|
||||
* on peut rendre une propriété obligatoire (les propriétés sont optionnelles par défaut si elles sont *nullable*) avec la méthode d'extension ```IsRequired()```.
|
||||
Dans l'exemple ci-dessous, le nom est obligatoire.
|
||||
*équivalent de ```[Required]``` devant la propriété ```Nom``` de ```Nounours```*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired();
|
||||
```
|
||||
* on peut imposer une taille max à une chaîne de caractères avec la méthode d'extension ```HasMaxLength(...)```.
|
||||
Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères.
|
||||
*équivalent de ```[MaxLength(256)]``` devant la propriété ```Nom``` de ```Nounours```*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasMaxLength(256);
|
||||
```
|
||||
* on peut chaîner les méthodes d'extension.
|
||||
Dans l'exemple ci-dessous, le nom est obligatoire et ne doit pas avoir plus de 256 caractères.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
```
|
||||
* on peut préciser qu'on ne veut pas qu'une propriété soit transformée en colonne de la table avec la méthode d'extension ```Ignore()```.
|
||||
Par exemple, ici, le nombre de poils ne sera pas mappé en colonne.
|
||||
*équivalent de ```[NotMapped]``` devant la propriété ```NbPoils``` de ```Nounours```*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
|
||||
```
|
||||
* on peut préciser qu'une propriété doit être utilisée en tant que clé primaire avec la méthode d'extension ```HasKey``` prenant en paramètre le nom de la propriété à prendre en compte.
|
||||
*équivalent de ```[Key]``` devant la propriété à utiliser en clé primaire*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
```
|
||||
On peut ensuite préciser qu'on veut que cette clé soit générée par la base lors de l'insertion à l'aide de la méthode d'extension ```ValueGeneratedOnAdd()```.
|
||||
*équivalent de ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]``` devant la propriété à utiliser en clé primaire*
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
```
|
||||
Notez que la clé n'est donc, contrairement aux conventions d'écriture, pas nécessairement un ```int```. Elle peut-être un ```Guid```, un ```string```, etc. Son nom peut-être autre chose que "ID".
|
||||
*Note : on peut aussi faire des clés composées, comme je le montrerai dans un autre exemple.*
|
||||
Dans l'exemple ci-dessous, un ```Nounours```possédera une clé unique de type ```Guid``` générée par la base et placée dans la colonne "UniqueId".
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
```
|
||||
|
||||
**En résumé**, avec la *Fluent API* :
|
||||
* on peut changer le nom de la table
|
||||
* on peut changer le nom et le type d'une colonne
|
||||
* on peut rendre une propriété obligatoire ou optionnelle
|
||||
* on peut empêcher le *mapping* d'une propriété
|
||||
* on peut imposer une taille max pour les chaînes de caractères
|
||||
* on peut transformer une propriété en clé primaire et demander à la base de la générée lors de l'insertion.
|
||||
|
||||
### La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours```
|
||||
```csharp title='Program.cs'
|
||||
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 };
|
||||
```
|
||||
* Elle démarre une connexion à la base de données
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide.
|
||||
Notez l'accès à la table de ```Nounours``` via ```db.NounoursSet```.
|
||||
Notez également que tant que ```SaveChanges``` n'est pas appelée, les suppressions ne sont pas effectives dans la base, seulement en local dans le programme.
|
||||
_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._
|
||||
```csharp title='Program.cs'
|
||||
if (db.NounoursSet.Count() > 0)
|
||||
{
|
||||
WriteLine("La base n'est pas vide !");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
WriteLine("début du nettoyage...");
|
||||
|
||||
foreach (var n in db.NounoursSet.ToArray())
|
||||
{
|
||||
WriteLine($"Suppression de {n}");
|
||||
db.NounoursSet.Remove(n);
|
||||
}
|
||||
|
||||
WriteLine("Base avant sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base.
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
|
||||
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_003_EF_CF_Fluent_API
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_003
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_003_EF_CF_Fluent_API**.
|
||||
|
||||
* Le résultat de l'exécution pourra ressembler à :
|
||||
```
|
||||
Base après ajout des 3 nounours et sauvegarde des changements :
|
||||
30817fff-8736-457d-db18-08d7908d7986: Chewbacca (27/05/1977, 1234567 poils)
|
||||
aa4469c4-a6c8-4077-db19-08d7908d7986: Yoda (21/05/1980, 3 poils)
|
||||
69cb5892-6750-4629-db1a-08d7908d7986: Ewok (25/05/1983, 3456789 poils)
|
||||
```
|
||||
:::note
|
||||
les identifiants seront bien sûr différents.
|
||||
:::
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_003_EF_CF_Fluent_API.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|UniqueId |Nom|Naissance
|
||||
|---|---|---
|
||||
|30817fff-8736-457d-db18-08d7908d7986|Chewbacca|27/05/1977
|
||||
|aa4469c4-a6c8-4077-db19-08d7908d7986|Yoda|21/05/1980
|
||||
|69cb5892-6750-4629-db1a-08d7908d7986|Ewok|25/05/1983
|
||||
|
||||
:::info
|
||||
les identifiants seront bien sûr différents.
|
||||
:::
|
||||
:::info
|
||||
Notez l'absence de la colonne "NbPoils"
|
||||
:::
|
||||
:::info
|
||||
Notez le nom de la colonne "Naissance" et le formatage de la date
|
||||
:::
|
@ -1,133 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.4. Keys with conventions (Entity Framework Code First)'
|
||||
sidebar_position: 4
|
||||
description: "explique comment créer les clés primaires d'une entité lorsqu'on utilise les conventions d'écriture"
|
||||
---
|
||||
|
||||
# Keys with conventions
|
||||
*06/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_004_Keys_conventions)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple traite des clés primaires associées aux entités.
|
||||
|
||||
:::info Prérequis
|
||||
Je n'explique pas à travers cet exemple les principes de base d'**Entity Framework Core** et en particulier les chaînes de connexion et le lien entre entité et table.
|
||||
Pour plus de renseignements sur :
|
||||
* les chaînes de connexion : [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings)
|
||||
* les liens entre entités et tables : [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions), [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations) et [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
:::
|
||||
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation des **conventions d'écriture**.
|
||||
|
||||
:::tip
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec les *data annotations* ici : [**2.5. Keys with data annotations**](/docs/Entity-Framework/Model/EF_CF_KeysDataAnnotations).
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : [**2.6. Keys with Fluent API**](/docs/Entity-Framework/Model/EF_CF_Keys_FluentAPI).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Les clés primaires
|
||||
Une clé permet de rendre unique chaque instance d'une entité. La plupart des entités n'ont qu'une seule clé qui est alors transformée en *clé primaire* pour les bases de données relationnelles.
|
||||
:::note
|
||||
une entité peut avoir d'autres clés, on parle d'__alternate keys__. Elles seront présentées dans les exemples sur les relations entre entités.
|
||||
:::
|
||||
Si on utilise les *conventions d'écriture*, une propriété pour être transformée en clé doit respecter les contraintes suivantes :
|
||||
* elle doit être nommée ```Id``` ou ```ID```,
|
||||
* elle doit être nommée ```<typeDeLEntite>Id```, e.g. ```NounoursId```.
|
||||
|
||||
Les autres contraintes sur une clé dans le cas de l'utilisation des *conventions d'écriture* sont :
|
||||
* elle doit être de type ```int```, ```string```, ```byte[]```. Toutefois, certains types nécessitent l'utilisation de converteurs pour être utilisés avec certains fournisseurs. Je conseille donc l'utilisation de ```int``` qui marche avec la grande majorité des fournisseurs.
|
||||
* elle est générée lors de l'insertion en base.
|
||||
|
||||
Les autres modes (*data annotations* et *Fluent API*) offrent plus de solutions quant à la gestion des clés.
|
||||
|
||||
## La classe ```Nounours```
|
||||
La classe ```Nounours``` utilise les conventions d'écriture.
|
||||
* Par défaut, les propriétés utilisées comme clés primaires sont en mode **Generated on add**.
|
||||
Une nouvelle valeur est donc générée lors de l'insertion d'une nouvelle entité en base. Les valeurs des autres propriétés ne sont pas générées lors de l'insertion ou de la mise à jour.
|
||||
* Dans cette classe, j'ai respecté la contrainte de nommage qui propose ```Id``` ou ```ID```
|
||||
```csharp title='Nounours.cs'
|
||||
public int ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
|
||||
## La classe ```Cylon```
|
||||
La classe ```Cylon``` utilise les conventions d'écriture.
|
||||
* Dans cette classe, j'ai respecté la contrainte de nommage qui propose ```<TypeDeLEntité>Id```
|
||||
```csharp title='Nounours.cs'
|
||||
public int CylonId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
|
||||
## La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours``` et de ```Cylon``` et les ajoute en base après avoir nettoyé les tables au préalables.
|
||||
* Elle affiche les ```Nounours``` et les ```Cylon```.
|
||||
:::tip
|
||||
Notez la génération des identifiants !
|
||||
:::
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_004_Keys_conventions
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_004
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_004_Keys_conventions**.
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_004_Keys_conventions.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Le résultat de l'exécution peut être :
|
||||
```
|
||||
database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :
|
||||
Nounours 1: Chewbacca (27/05/1977, 1234567 poils)
|
||||
Nounours 2: Yoda (21/05/1980, 3 poils)
|
||||
Nounours 3: Ewok (25/05/1983, 3456789 poils)
|
||||
Cylon 1: John Cavil, Number 1
|
||||
Cylon 2: Leoben Conoy, Number 2
|
||||
Cylon 3: D'Anna Biers, Number 3
|
||||
Cylon 4: Simon, Number 4
|
||||
Cylon 5: Aaron Doral, Number 5
|
||||
Cylon 6: Caprica 6, Number 6
|
||||
Cylon 7: Daniel, Number 7
|
||||
Cylon 8: Boomer, Number 8
|
||||
Cylon 9: Athena, Number 8
|
||||
```
|
||||
:::note
|
||||
les identifiants peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données.
|
||||
:::
|
@ -1,148 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.5. Keys with data annotations (Entity Framework Code First)'
|
||||
sidebar_position: 5
|
||||
description: "explique comment créer les clés primaires d'une entité lorsqu'on utilise les *data annotations"
|
||||
---
|
||||
|
||||
# Keys with data annotations
|
||||
*06/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple traite des clés primaires associées aux entités.
|
||||
|
||||
:::info Prérequis
|
||||
Je n'explique pas à travers cet exemple les principes de base d'**Entity Framework Core** et en particulier les chaînes de connexion et le lien entre entité et table.
|
||||
Pour plus de renseignements sur :
|
||||
* les chaînes de connexion : [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings)
|
||||
* les liens entre entités et tables : [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions), [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations) et [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
:::
|
||||
|
||||
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation des **data annotations**.
|
||||
:::tip
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : [**2.4. Keys with conventions**](/docs/Entity-Framework/Model/EF_CF_KeysConvention).
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : [**2.6. Keys with Fluent API**](/docs/Entity-Framework/Model/EF_CF_Keys_FluentAPI).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Les clés primaires
|
||||
Une clé permet de rendre unique chaque instance d'une entité. La plupart des entités n'ont qu'une seule clé qui est alors transformée en *clé primaire* pour les bases de données relationnelles.
|
||||
*Note: une entité peut avoir d'autres clés, on parle d'__alternate keys__. Elles seront présentées dans les exemples sur les relations entre entités.*
|
||||
Si on utilise les *data annotations*, une propriété pour être transformée en clé doit respecter les contraintes suivantes :
|
||||
* aucune contrainte sur le nommage de la propriété ; j'ai par exemple choisi ```UniqueId``` pour ```Nounours```, et ```FrakId``` pour ```Cylon```.
|
||||
* elle doit être précédée de l'annotation ```[Key]```
|
||||
* elle peut être générée par la base et dans ce cas elle doit être précédée de l'annotation ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]```, ou ne pas être générée par la base et dans ce cas être précédée de l'annotation ```[DatabaseGenerated(DatabaseGeneratedOption.None)]```.
|
||||
Dans ce dernier cas, c'est à l'utilisateur de gérer ses propres clés et leur unicité dans la base.
|
||||
* elle peut être de différents types ```int```, ```string```, ```Guid```, ```byte[]```... attention toutefois si vous choisissez de laisser la table générer les valeurs car certains fournisseurs ne savent pas générer tous les types.
|
||||
|
||||
## La classe ```Nounours```
|
||||
La classe ```Nounours``` utilise les *data annotations* et laisse le soin à la base de générer les clés uniques.
|
||||
```csharp title='Nounours.cs'
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int UniqueId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
|
||||
## La classe ```Cylon```
|
||||
La classe ```Cylon``` utilise les *data annotations* et laisse le soin à l'utilisateur de gérer ses clés uniques.
|
||||
```csharp title='Nounours.cs'
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public int FrakId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
|
||||
## La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours``` et de ```Cylon``` et les ajoute en base après avoir nettoyé les tables au préalables.
|
||||
Notez que l'utilisateur n'a pas besoin de donner une valeur à ```Nounours.UniqueId``` puisque la base s'en charge, alors qu'il doit donner une valeur à ```Cylon.FrakId``` car la base de ne génère pas les clés.
|
||||
Si vous ne donnez pas une valeur à ```Cylon.FrakId```, alors la valeur par défaut est donnée (```0```). Il n'y aura pas de problème si cet identifiant n'a pas été donné, mais dès le deuxième ```Cylon```, vous aurez une exception.
|
||||
:::note
|
||||
la valeur par défaut pour ```int``` est ```0``` ; pour ```Guid```, ```Guid.Empty``` ; pour ```string```, ```null```...
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
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 };
|
||||
|
||||
Cylon c1 = new Cylon { FrakId = 2, Name = "John Cavil", Generation = 1 };
|
||||
Cylon c2 = new Cylon { FrakId = 4, Name = "Leoben Conoy", Generation = 2 };
|
||||
Cylon c3 = new Cylon { FrakId = 6, Name = "D'Anna Biers", Generation = 3 };
|
||||
Cylon c4 = new Cylon { FrakId = 8, Name = "Simon", Generation = 4 };
|
||||
Cylon c5 = new Cylon { FrakId = 10, Name = "Aaron Doral", Generation = 5 };
|
||||
Cylon c6 = new Cylon { FrakId = 12, Name = "Caprica 6", Generation = 6 };
|
||||
Cylon c7 = new Cylon { FrakId = 14, Name = "Daniel", Generation = 7 };
|
||||
Cylon c8 = new Cylon { FrakId = 16, Name = "Boomer", Generation = 8 };
|
||||
Cylon c9 = new Cylon { FrakId = 17, Name = "Athena", Generation = 8 };
|
||||
```
|
||||
* Elle affiche les ```Nounours``` et les ```Cylon```.
|
||||
:::tip
|
||||
Notez la génération des identifiants pour la classe ```Nounours``` uniquement : si vous exécutez plusieurs fois l'exemple, les clés des ```Nounours``` changent mais pas celles des ```Cylon```.
|
||||
:::
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_005_Keys_data_annotations
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration ex_042_005
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_005_Keys_data_annotations**.
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_005_Keys_data_annotations.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Le résultat de l'exécution peut être :
|
||||
```
|
||||
database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :
|
||||
Nounours 1: Chewbacca (27/05/1977, 1234567 poils)
|
||||
Nounours 2: Yoda (21/05/1980, 3 poils)
|
||||
Nounours 3: Ewok (25/05/1983, 3456789 poils)
|
||||
Cylon 2: John Cavil, Number 1
|
||||
Cylon 4: Leoben Conoy, Number 2
|
||||
Cylon 6: D'Anna Biers, Number 3
|
||||
Cylon 8: Simon, Number 4
|
||||
Cylon 10: Aaron Doral, Number 5
|
||||
Cylon 12: Caprica 6, Number 6
|
||||
Cylon 14: Daniel, Number 7
|
||||
Cylon 16: Boomer, Number 8
|
||||
Cylon 17: Athena, Number 8
|
||||
```
|
||||
:::note
|
||||
les identifiants des ```Nounours``` peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données, mais pas ceux des ```Cylon``` puisqu'ils sont gérés par l'utilisateur.
|
||||
:::
|
@ -1,217 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.6. Keys with Fluent API'
|
||||
sidebar_position: 6
|
||||
description: "explique comment créer les clés primaires d'une entité lorsqu'on utilise la Fluent API"
|
||||
---
|
||||
|
||||
# Keys with Fluent API
|
||||
*07/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple traite des clés primaires associées aux entités.
|
||||
|
||||
:::info Prérequis
|
||||
Je n'explique pas à travers cet exemple les principes de base d'**Entity Framework Core** et en particulier les chaînes de connexion et le lien entre entité et table.
|
||||
Pour plus de renseignements sur :
|
||||
* les chaînes de connexion : [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings)
|
||||
* les liens entre entités et tables : [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions), [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations) et [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
:::
|
||||
|
||||
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation de la **Fluent API**.
|
||||
|
||||
:::tip
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : [**2.4. Keys with conventions**](/docs/Entity-Framework/Model/EF_CF_KeysConvention).
|
||||
Vous pourrez trouver une version plus ou moins équivalente avec la *data annotations* ici : [**2.5. Keys with data annotations**](/docs/Entity-Framework/Model/EF_CF_KeysDataAnnotations).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Les clés primaires
|
||||
Une clé permet de rendre unique chaque instance d'une entité. La plupart des entités n'ont qu'une seule clé qui est alors transformée en *clé primaire* pour les bases de données relationnelles.
|
||||
:::note
|
||||
Une entité peut avoir d'autres clés, on parle d'__alternate keys__. Elles seront présentées dans les exemples sur les relations entre entités.
|
||||
:::
|
||||
|
||||
Si on utilise la *Fluent API*, une propriété pour être transformée en clé doit respecter les contraintes suivantes
|
||||
(toutes les modifications se font dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```) :
|
||||
* aucune contrainte sur le nommage de la propriété ; j'ai par exemple choisi ```UniqueId``` pour ```Nounours```, et ```FrakId``` pour ```Cylon```.
|
||||
* on définit qu'une propriété est une clé primaire avec la méthode d'extension ```HasKey``` sur la classe ```Entity```.
|
||||
```csharp
|
||||
//définition de la clé primaire de Nounours
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
```
|
||||
```csharp
|
||||
//définition de la clé primaire de Cylon
|
||||
modelBuilder.Entity<Cylon>().HasKey(c => c.FrakId);
|
||||
```
|
||||
* elle peut être générée par la base et dans ce cas, on ajoute à la ```Property``` la méthode d'extension ```ValueGeneratedOnAdd()```.
|
||||
```csharp
|
||||
//définition du mode de génération de la clé : génération à l'insertion
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
```
|
||||
* elle peut ne pas être générée par la base et être gérée par l'utilisateur, dans ce cas on utilise la méthode d'extension ```ValueGeneratedNever()```.
|
||||
Dans ce dernier cas, c'est à l'utilisateur de gérer ses propres clés et leur unicité dans la base.
|
||||
```csharp
|
||||
//définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base)
|
||||
modelBuilder.Entity<Cylon>().Property(c => c.FrakId).ValueGeneratedNever();
|
||||
```
|
||||
* elle peut être de différents types ```int```, ```string```, ```Guid```, ```byte[]```... attention toutefois si vous choisissez de laisser la table générer les valeurs car certains fournisseurs ne savent pas générer tous les types.
|
||||
|
||||
## Les clés primaires composites
|
||||
En *Fluent API* (et seulement en *Fluent API*), on peut définir des clés composites, c'est-à-dire se basant sur plusieurs propriétés.
|
||||
Pour cela, on utilise également ```HasKey```et on l'associe à un type anonyme, comme par exemple pour la classe ```Ordinateur``` dans cet exemple, dont la clé est un composite des propriétés ```CodeId``` et ```Modele```.
|
||||
Ainsi, dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```, on ajoute :
|
||||
```csharp
|
||||
//définition d'une clé primaire composite pour Ordinateur
|
||||
modelBuilder.Entity<Ordinateur>().HasKey(o => new { o.CodeId, o.Modele });
|
||||
```
|
||||
Les clés composites ne peuvent pas être générées par la base.
|
||||
|
||||
## Les classes POCO ```Nounours```, ```Cylon``` et ```Ordinateur```
|
||||
Lorsqu'on utilise la *Fluent API*, ces classes n'ont aucune annotation. La classe ```Ordinateur``` n'a pas de propriété "Id".
|
||||
|
||||
## La classe ```DBEntites : DbContext```
|
||||
La classe dérivant de ```DbContext``` ressemble dès lors dans notre exemple à :
|
||||
```csharp title='DBEntities.cs'
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ex_042_006_Keys_FluentAPI
|
||||
{
|
||||
class DBEntities : DbContext
|
||||
{
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
public DbSet<Cylon> CylonsSet { get; set; }
|
||||
public DbSet<Ordinateur> Ordinateurs { get; set; }
|
||||
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_Keys_FluentAPI.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
//définition de la clé primaire de Nounours
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
//définition du mode de génération de la clé : génération à l'insertion
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
|
||||
//définition de la clé primaire de Cylon
|
||||
modelBuilder.Entity<Cylon>().HasKey(c => c.FrakId);
|
||||
//définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base)
|
||||
modelBuilder.Entity<Cylon>().Property(c => c.FrakId).ValueGeneratedNever();
|
||||
|
||||
//définition d'une clé primaire composite pour Ordinateur
|
||||
modelBuilder.Entity<Ordinateur>().HasKey(o => new { o.CodeId, o.Modele });
|
||||
//une clé composite ne peut pas être générée par la base
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours```, de ```Cylon``` et d'```Ordinateur``` et les ajoute en base après avoir nettoyé les tables au préalables.
|
||||
Notez que l'utilisateur n'a pas besoin de donner une valeur à ```Nounours.UniqueId``` puisque la base s'en charge, alors qu'il doit donner une valeur à ```Cylon.FrakId``` car la base de ne génère pas les clés.
|
||||
Si vous ne donnez pas une valeur à ```Cylon.FrakId```, alors la valeur par défaut est donnée (```0```). Il n'y aura pas de problème si cet identifiant n'a pas été donné, mais dès le deuxième ```Cylon```, vous aurez une exception.
|
||||
:::note
|
||||
La valeur par défaut pour ```int``` est ```0``` ; pour ```Guid```, ```Guid.Empty``` ; pour ```string```, ```null```...
|
||||
:::
|
||||
Notez que l'utilisateur doit garantir pour ses instances d'```Ordinateur``` l'unicité des couples ```CodeId```/```Modele```, puisqu'il s'agit d'une clé composite.
|
||||
```csharp title='Program.cs'
|
||||
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 };
|
||||
|
||||
Cylon c1 = new Cylon { FrakId = 2, Name = "John Cavil", Generation = 1 };
|
||||
Cylon c2 = new Cylon { FrakId = 4, Name = "Leoben Conoy", Generation = 2 };
|
||||
Cylon c3 = new Cylon { FrakId = 6, Name = "D'Anna Biers", Generation = 3 };
|
||||
Cylon c4 = new Cylon { FrakId = 8, Name = "Simon", Generation = 4 };
|
||||
Cylon c5 = new Cylon { FrakId = 10, Name = "Aaron Doral", Generation = 5 };
|
||||
Cylon c6 = new Cylon { FrakId = 12, Name = "Caprica 6", Generation = 6 };
|
||||
Cylon c7 = new Cylon { FrakId = 14, Name = "Daniel", Generation = 7 };
|
||||
Cylon c8 = new Cylon { FrakId = 16, Name = "Boomer", Generation = 8 };
|
||||
Cylon c9 = new Cylon { FrakId = 17, Name = "Athena", Generation = 8 };
|
||||
|
||||
Ordinateur o1 = new Ordinateur { Année = 2019, Modele = "MacBook Pro", CodeId = "IUT_1" };
|
||||
Ordinateur o2 = new Ordinateur { Année = 2017, Modele = "MacBook Pro", CodeId = "IUT_2" };
|
||||
Ordinateur o3 = new Ordinateur { Année = 2016, Modele = "MacBook Pro", CodeId = "IUT_4" };
|
||||
Ordinateur o4 = new Ordinateur { Année = 2019, Modele = "Dell Latitude", CodeId = "IUT_1" };
|
||||
Ordinateur o5 = new Ordinateur { Année = 2012, Modele = "Dell Latitude", CodeId = "IUT_2" };
|
||||
Ordinateur o6 = new Ordinateur { Année = 2013, Modele = "Dell Latitude", CodeId = "IUT_3" };
|
||||
```
|
||||
* Elle affiche les ```Nounours```, les ```Cylon``` et les ```Ordinateur```.
|
||||
:::note
|
||||
Notez la génération des identifiants pour la classe ```Nounours``` uniquement : si vous exécutez plusieurs fois l'exemple, les clés des ```Nounours``` changent mais pas celles des ```Cylon```.
|
||||
:::
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_006_Keys_FluentAPI
|
||||
```
|
||||
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration ex_042_006
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_006_Keys_FluentAPI**.
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_006_Keys_FluentAPI.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Le résultat de l'exécution peut être :
|
||||
```
|
||||
database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :
|
||||
Nounours 1: Chewbacca (27/05/1977, 1234567 poils)
|
||||
Nounours 2: Ewok (25/05/1983, 3456789 poils)
|
||||
Nounours 3: Yoda (21/05/1980, 3 poils)
|
||||
Cylon 2: John Cavil, Number 1
|
||||
Cylon 4: Leoben Conoy, Number 2
|
||||
Cylon 6: D'Anna Biers, Number 3
|
||||
Cylon 8: Simon, Number 4
|
||||
Cylon 10: Aaron Doral, Number 5
|
||||
Cylon 12: Caprica 6, Number 6
|
||||
Cylon 14: Daniel, Number 7
|
||||
Cylon 16: Boomer, Number 8
|
||||
Cylon 17: Athena, Number 8
|
||||
Computer IUT_1 (Dell Latitude, 2019)
|
||||
Computer IUT_1 (MacBook Pro, 2019)
|
||||
Computer IUT_2 (Dell Latitude, 2012)
|
||||
Computer IUT_2 (MacBook Pro, 2017)
|
||||
Computer IUT_3 (Dell Latitude, 2013)
|
||||
Computer IUT_4 (MacBook Pro, 2016)
|
||||
```
|
||||
:::note
|
||||
Les identifiants des ```Nounours``` peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données, mais pas ceux des ```Cylon``` puisqu'ils sont gérés par l'utilisateur.
|
||||
:::
|
@ -1,326 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.2. Data Annotations (Entity Framework Code First)'
|
||||
sidebar_position: 2
|
||||
description: "explique comment utiliser les data annotations pour personnaliser la transformation d'une entité en table"
|
||||
---
|
||||
|
||||
# Data Annotations
|
||||
*03/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations)
|
||||
|
||||
---
|
||||
|
||||
Le lien entre une base de données et vos classes à l'aide de **Entity Framework Core** se fait via une classe dérivant de ```DbContext```.
|
||||
Cette classe doit contenir des ```DbSet<T>```. Chaque ```DbSet<T>``` correspond à une table et ```T``` correspond à une de vos classes qu'on appelle *entité*.
|
||||
Le lien entre les tables et les entités est géré plus ou moins automatiquement par le *framework*.
|
||||
**EF Core** permet de lire/écrire les instances d'entité de la base de données ; permet de créer des tables pour les entités via les migrations (pour les bases de données relationnelles) ;
|
||||
les types exposés dans les propriétés d'une entité deviennent automatiquement des entités (mais pas forcément des tables)
|
||||
|
||||
**Entity Framework Core** propose 3 solutions différentes pour relier des entités à des tables de la base de données :
|
||||
* **conventions d'écriture** : c'est la solution la plus simple, qui analyse le code de votre classe pour en déduire de quelle manière la relier à la table.
|
||||
* **data annotations** : elle se base sur des décorateurs (appelés *data annotations*) que vous placez autour de vos propriétés pour indiquer de quelle manière les relier aux colonnes de votre table. Les *data annotations* écrasent les conventions d'écriture.
|
||||
* **Fluent API** : directement dans le code de votre ```DbContext``` (plus précisément, dans la méthode ```OnModelCreating```), vous précisez comment se fait le *mapping* entre votre entité et votre table. La *Fluent API* écrase les conventions d'écriture et les *data annotations*.
|
||||
|
||||
En d'autres termes, si vous n'écrivez rien, **EF Core** utilise les conventions d'écriture ; si vous n'utilisez que des *data annotations*, elles sont prioritaires sur les convetions d'écriture ; si vous utilisez la *Fluent API*, elle est prioritaire sur les deux autres méthodes.
|
||||
Mais on peut faire un mix des différentes méthodes.
|
||||
|
||||
Cet exemple montre de quelle manière les **data annotations** sont utilisées pour transformer une entité en table.
|
||||
Il montre notamment :
|
||||
* comment choisir le nom de la table
|
||||
* comment ignorer une propriété de l'entité
|
||||
* comment le nom et le type d'une colonne de la table peuvent être modifiés
|
||||
* comment un identifiant unique est choisi et généré.
|
||||
|
||||
Cet exemple est répété d'une manière très similaire en utilisant les autres méthodes de *mapping* entre entité et table :
|
||||
* [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions)
|
||||
* [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient deux classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
* Cette classe dérive de ```DbContext```. Elle contient des ```DbSet<T>``` où ```T``` est une entité. Elle contient autant de ```DbSet<T>``` que de tables.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
class NounoursDBEntities : DbContext
|
||||
{
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
}
|
||||
```
|
||||
ici, on indique donc qu'il y aura une table de ```Nounours```.
|
||||
* **EF Core** utilisera cette classe pour faire la création de la base de données et de ses tables. Pour cela, elle utilise la chaîne de connexion donnée dans la méthode ```OnConfiguring```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_001_EF_CF_conventions.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
```
|
||||
=> cf **ex_041_001_ConnectionStrings** pour en savoir plus sur les chaîne de connexion
|
||||
* Dans cet exemple, la table de ```Nounours``` est créée en utilisant les *data annotations* et les conventions d'écriture de la classe ```Nounours```.
|
||||
|
||||
### La classe ```Nounours```
|
||||
* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object.
|
||||
```csharp title='Nounours.cs'
|
||||
[Table("TableNounours")]
|
||||
public class Nounours
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid UniqueId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Required]
|
||||
[MaxLength(256)]
|
||||
//[Column("name", Order=0, TypeName ="varchar(200)")]
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Column("Naissance", TypeName = "date")]
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle contient 3 propriétés en lecture/écriture : ```Nom```, ```DateDeNaissance``` et ```NbPoils```. Nous parlerons de la 4ème (```UniqueId```) dans un moment.
|
||||
* **Entity Framework Core** va utiliser la classe POCO ```Nounours``` pour créer une table dans la base de données, lorsque le ```DbSet``` va être créé.
|
||||
* S'il y a des *data annotations*, elles sont prioritaires sur l'utilisation des conventions d'écriture d'Entity Framework.
|
||||
|
||||
Un petit tour des annotations utilisées dans l'exemple et quelques indications sur celles manquantes :
|
||||
* la classe ```Nounours``` est précédée de l'annotation ```[Table("TableNounours")]``` : la table créée n'aura donc pas le nom de la classe (comme le permettent les conventions d'écriture), mais le nom indiqué dans l'annotation :
|
||||
```csharp title='Nounours.cs'
|
||||
[Table("TableNounours")]
|
||||
public class Nounours
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
* on peut changer le nom, le type ou l'ordre d'une colonne en précédant une propriété de l'annotation ```[Column(...)]```. Dans l'exemple ci-dessous,
|
||||
la colonne ne s'appellera pas "DateDeNaissance" (comme le permettraient les conventions d'écriture), mais "Naissance" grâce à l'annotation.
|
||||
```csharp title='Nounours.cs'
|
||||
[Column("Naissance")]
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* On pourrait aller plus loin en indiquant l'ordre et le changement de type, par exemple :
|
||||
```csharp title='Nounours.cs'
|
||||
[Column("name", Order=0, TypeName=varchar(200))]
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* Dans la classe ```Nounours```, la propriété ```DateDeNaissance``` est annotée de façon à modifier le type par défaut de *mapping* (la partie *time* sera évincée) et le nom de colonne sera modifé en "Naissance".
|
||||
```csharp title='Nounours.cs'
|
||||
[Column("Naissance", TypeName = "date")]
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* L'annotation ```[Required]``` permet de rendre obligatoire une propriété. Une propriété peut être optionnelle seulement si elle peut avoir la valeur ```null```. Dans l'exemple ci-dessous, le nom est obligatoire.
|
||||
```csharp title='Nounours.cs'
|
||||
[Required]
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* L'annotation ```[MaxLength(256)]``` permet d'imposer une taille max à une chaîne de caractères. Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères.
|
||||
```csharp title='Nounours.cs'
|
||||
[MaxLength(256)]
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* L'annotation ```[NotMapped]``` permet d'indiquer qu'une propriété ne doit pas être mappée en colonne de table. Par exemple, ici, le nombre de poils ne sera pas mappé en colonne.
|
||||
```csharp title='Nounours.cs'
|
||||
[NotMapped]
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
```
|
||||
* L'annotation ```[Key]``` transforme n'importe quelle propriété en clé primaire.
|
||||
Si on ajoute l'annotation ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]```, on précise par exemple que la clé primaire sera générée par la base, lors de l'insertation dans la table.
|
||||
Notez que la clé n'est donc, contrairement aux conventions d'écriture, pas nécessairement un ```int```. Elle peut-être un ```Guid```, un ```string```, etc. Son nom peut-être autre chose que "ID".
|
||||
:::note
|
||||
on peut aussi faire des clés composées, comme je le montrerai dans un autre exemple.
|
||||
:::
|
||||
Dans l'exemple ci-dessous, un ```Nounours```possédera une clé unique de type ```Guid``` générée par la base et placée dans la colonne "UniqueId".
|
||||
```csharp title='Nounours.cs'
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid UniqueId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
|
||||
**En résumé**, avec les *data annotations* :
|
||||
* on peut changer le nom de la table
|
||||
* on peut changer le nom, le type et l'ordre d'une colonne
|
||||
* on peut rendre une propriété obligatoire ou optionnelle
|
||||
* on peut empêcher le *mapping* d'une propriété
|
||||
* on peut imposer une taille max pour les chaînes de caractères
|
||||
* on peut transformer une propriété en clé primaire et demander à la base de la générée lors de l'insertion.
|
||||
|
||||
### La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours```
|
||||
```csharp title='Program.cs'
|
||||
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 };
|
||||
```
|
||||
* Elle démarre une connexion à la base de données
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide.
|
||||
Notez l'accès à la table de ```Nounours``` via ```db.NounoursSet```.
|
||||
Notez également que tant que ```SaveChanges``` n'est pas appelée, les suppressions ne sont pas effectives dans la base, seulement en local dans le programme.
|
||||
_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._
|
||||
```csharp title='Program.cs'
|
||||
if (db.NounoursSet.Count() > 0)
|
||||
{
|
||||
WriteLine("La base n'est pas vide !");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
WriteLine("début du nettoyage...");
|
||||
|
||||
foreach (var n in db.NounoursSet.ToArray())
|
||||
{
|
||||
WriteLine($"Suppression de {n}");
|
||||
db.NounoursSet.Remove(n);
|
||||
}
|
||||
|
||||
WriteLine("Base avant sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base.
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
|
||||
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_002_EF_CF_data_annotations
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_002
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_002_EF_CF_data_annotations**.
|
||||
|
||||
* Le résultat de l'exécution peut ressembler à :
|
||||
```
|
||||
Base après ajout des 3 nounours et sauvegarde des changements :
|
||||
fbc3f7c5-a333-4d07-f7f2-08d7907e5437: Chewbacca (27/05/1977, 1234567 poils)
|
||||
63b18e10-a683-4144-f7f3-08d7907e5437: Yoda (21/05/1980, 3 poils)
|
||||
c4eab29b-315b-416e-f7f4-08d7907e5437: Ewok (25/05/1983, 3456789 poils)
|
||||
```
|
||||
:::note
|
||||
les identifiants seront bien sûr différents
|
||||
:::
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_002_EF_CF_data_annotations.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|UniqueId |Nom|Naissance
|
||||
|---|---|---
|
||||
|fbc3f7c5-a333-4d07-f7f2-08d7907e5437|Chewbacca|27/05/1977
|
||||
|63b18e10-a683-4144-f7f3-08d7907e5437|Yoda|21/05/1980
|
||||
|c4eab29b-315b-416e-f7f4-08d7907e5437|Ewok|25/05/1983
|
||||
|
||||
:::info
|
||||
les identifiants seront bien sûr différents.
|
||||
:::
|
||||
:::info
|
||||
Notez l'absence de la colonne "NbPoils"
|
||||
:::
|
||||
:::info
|
||||
Notez le nom de la colonne "Naissance" et le formatage de la date
|
||||
:::
|
@ -1,236 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.1. Naming conventions (Entity Framework Code First)'
|
||||
sidebar_position: 1
|
||||
description: "explique quelles sont les conventions d'écriture utilisées pour la transformation d'une entité en table"
|
||||
---
|
||||
|
||||
# Entity Framework CodeFirst Conventions
|
||||
*02/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions)
|
||||
|
||||
---
|
||||
|
||||
Le lien entre une base de données et vos classes à l'aide de **Entity Framework Core** se fait via une classe dérivant de ```DbContext```.
|
||||
Cette classe doit contenir des ```DbSet<T>```. Chaque ```DbSet<T>``` correspond à une table et ```T``` correspond à une de vos classes qu'on appelle *entité*.
|
||||
Le lien entre les tables et les entités est géré plus ou moins automatiquement par le *framework*.
|
||||
**EF Core** permet de lire/écrire les instances d'entité de la base de données ; permet de créer des tables pour les entités via les migrations (pour les bases de données relationnelles) ;
|
||||
les types exposés dans les propriétés d'une entité deviennent automatiquement des entités (mais pas forcément des tables)
|
||||
|
||||
**Entity Framework Core** propose 3 solutions différentes pour relier des entités à des tables de la base de données :
|
||||
* **conventions d'écriture** : c'est la solution la plus simple, qui analyse le code de votre classe pour en déduire de quelle manière la relier à la table.
|
||||
* **data annotations** : elle se base sur des décorateurs (appelés *data annotations*) que vous placez autour de vos propriétés pour indiquer de quelle manière les relier aux colonnes de votre table. Les *data annotations* écrasent les conventions d'écriture.
|
||||
* **Fluent API** : directement dans le code de votre ```DbContext``` (plus précisément, dans la méthode ```OnModelCreating```), vous précisez comment se fait le *mapping* entre votre entité et votre table. La *Fluent API* écrase les conventions d'écriture et les *data annotations*.
|
||||
|
||||
En d'autres termes, si vous n'écrivez rien, **EF Core** utilise les conventions d'écriture ; si vous n'utilisez que des *data annotations*, elles sont prioritaires sur les convetions d'écriture ; si vous utilisez la *Fluent API*, elle est prioritaire sur les deux autres méthodes.
|
||||
Mais on peut faire un mix des différentes méthodes.
|
||||
|
||||
Cet exemple montre de quelle manière les **conventions d'écriture** sont utilisées pour transformer une entité en table.
|
||||
Il montre notamment :
|
||||
* comment le nom de la table est choisi
|
||||
* s'il est possible d'ignorer une propriété de l'entité
|
||||
* comment le nom et le type d'une colonne de la table sont choisis
|
||||
* comment un identifiant unique est choisi et généré.
|
||||
|
||||
Cet exemple est répété d'une manière très similaire en utilisant les autres méthodes de *mapping* entre entité et table :
|
||||
* [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations)
|
||||
* [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient deux classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
* Cette classe dérive de ```DbContext```. Elle contient des ```DbSet<T>``` où ```T``` est une entité. Elle contient autant de ```DbSet<T>``` que de tables.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
class NounoursDBEntities : DbContext
|
||||
{
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
}
|
||||
```
|
||||
ici, on indique donc qu'il y aura une table de ```Nounours```.
|
||||
* **EF Core** utilisera cette classe pour faire la création de la base de données et de ses tables. Pour cela, elle utilise la chaîne de connexion donnée dans la méthode ```OnConfiguring```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_001_EF_CF_conventions.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
```
|
||||
=> cf **ex_041_001_ConnectionStrings** pour en savoir plus sur les chaîne de connexion
|
||||
* Dans cet exemple, la table de ```Nounours``` est créée en utilisant les conventions d'écriture de la classe ```Nounours```.
|
||||
|
||||
### La classe ```Nounours```
|
||||
* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object.
|
||||
```csharp title='Nounours.cs'
|
||||
public class Nounours
|
||||
{
|
||||
public int ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle contient 3 propriétés en lecture/écriture : ```Nom```, ```DateDeNaissance``` et ```NbPoils```. Nous parlerons de la 4ème (```ID```) dans un moment.
|
||||
* **Entity Framework Core** va utiliser la classe POCO ```Nounours``` pour créer une table dans la base de données, lorsque le ```DbSet``` va être créé.
|
||||
* L'utilisation des conventions d'écriture d'Entity Framework font que la table qui va être créée aura :
|
||||
* un nom correspondant au nom de la classe POCO : ici "Nounours"
|
||||
* une colonne pour chaque propriété publique : ici, "Nom", "DateDeNaissance" et "NbPoils"
|
||||
* De plus, en rajoutant une propriété de type int et avec le nom "ID", Entity Framework ajoute directement une colonne "ID" et l'utilise comme clé primaire.
|
||||
|
||||
**En résumé** :
|
||||
* le nom de la table est choisi automatiquement (c'est le nom de l'entité)
|
||||
* toutes les propriétés ayant un getter et un setter publiques sont des colonnes de la table
|
||||
* le nom des colonnes est choisi automatiquement (il s'agit du nom des propriétés)
|
||||
* il n'est pas possible d'ignorer une propriété
|
||||
* le type d'une colonne est imposé par un *mapping* automatique entre les types .NET et ceux de la base de données. Par exemple :
|
||||
* un ```DateTime``` est transformé en ```datetime2(7)```
|
||||
* un ```string``` est transformé en ```nvarchar(max)```
|
||||
* si c'est une clé, elle est transformée en ```nvarchar(450)```
|
||||
* ...
|
||||
* si la classe possède une propriété de type ```int``` s'appelant ```ID```, elle est automatiquement utilisée comme clé primaire générée par lz base de données lors de l'insertion
|
||||
|
||||
### La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours```
|
||||
```csharp title='Program.cs'
|
||||
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 };
|
||||
```
|
||||
* Elle démarre une connexion à la base de données
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide.
|
||||
Notez l'accès à la table de ```Nounours``` via ```db.NounoursSet```.
|
||||
Notez également que tant que ```SaveChanges``` n'est pas appelée, les suppressions ne sont pas effectives dans la base, seulement en local dans le programme.
|
||||
_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._
|
||||
```csharp title='Program.cs'
|
||||
if (db.NounoursSet.Count() > 0)
|
||||
{
|
||||
WriteLine("La base n'est pas vide !");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
WriteLine("début du nettoyage...");
|
||||
|
||||
foreach (var n in db.NounoursSet.ToArray())
|
||||
{
|
||||
WriteLine($"Suppression de {n}");
|
||||
db.NounoursSet.Remove(n);
|
||||
}
|
||||
|
||||
WriteLine("Base avant sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base.
|
||||
```csharp
|
||||
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
|
||||
|
||||
db.SaveChanges();
|
||||
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_001_EF_CF_conventions
|
||||
```
|
||||
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_001
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_001_EF_CF_conventions**.
|
||||
|
||||
* Le résultat de l'exécution doit ressembler à :
|
||||
```
|
||||
Base après ajout des 3 nounours et sauvegarde des changements :
|
||||
1: Chewbacca (27/05/1977, 1234567 poils)
|
||||
2: Yoda (21/05/1980, 3 poils)
|
||||
3: Ewok (25/05/1983, 3456789 poils)
|
||||
```
|
||||
|
||||
:::note
|
||||
les identifiants peuvent varier en fonction du nombre d'exécutions
|
||||
:::
|
||||
|
||||
* Comment vérifier le contenu des bases de données SQL Server ?
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_001_EF_CF_conventions.NounoursSet.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|ID |Nom|DateDeNaissance|NbPoils
|
||||
|---|---|---|---
|
||||
|1|Chewbacca|27/05/1977 00:00:00|1234567
|
||||
|2|Yoda|21/05/1980 00:00:00|3
|
||||
|3|Ewok|25/05/1983 00:00:00|3456789
|
||||
|
||||
:::note
|
||||
les identifiants peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données.
|
||||
:::
|
@ -1,233 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.1. Single Property Navigation with naming conventions'
|
||||
sidebar_position: 2
|
||||
description: "explique comment relier deux entités simplement avec les conventions de nommage"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# Single Property Navigation conventions
|
||||
|
||||
*18/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_010_SinglePropertyNavigation_conventions)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment réaliser une navigation entre deux entités avec une propriété simple (sens unique) avec Entity Framework Core et l'*annotation de données* ou les *conventions d'écriture*.
|
||||
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
|
||||
[**2.10.2. : Single Property navigation with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_02_SinglePropertyNavigation_fluentAPI)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Nounours```
|
||||
* ```Information```
|
||||
* ```NounoursDBEntities```
|
||||
* ```NounoursDBEntitiesWithStub```
|
||||
|
||||
### Les classes entités : ```Nounours``` et ```Information```
|
||||
|
||||
Un ```Nounours``` contient différentes propriétés (```Nom```, ```DateDeNaissance```, ```NbPoils```) dont ```Information``` de type ```Information```.
|
||||
Une ```Information``` possède deux propriétés de type ```string```, mais aucun lien vers ```Nounours```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Nounours{
|
||||
+UniqueId: Guid
|
||||
+Nom: string
|
||||
+DateDeNaissance: DateTime
|
||||
+NbPoils: int
|
||||
}
|
||||
class Information{
|
||||
+InformationId: int
|
||||
+MadeBy: string
|
||||
+MadeIn: string
|
||||
}
|
||||
Nounours--> "1" Information : +Information
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Nounours``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _data annotation_).
|
||||
* ```Information``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _data annotation_).
|
||||
* ```Nounours``` possède une association vers ```Information```
|
||||
```csharp title='Nounours.cs'
|
||||
public Information Information
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
* ```Information``` __NE__ possède __PAS__ d'association vers ```Nounours```
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```NounoursDBEntities``` dérive de ```DbContext```.
|
||||
* ```NounoursDBEntities``` déclare un seul ```DbSet``` de ```Nounours```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
```
|
||||
* Dans cet exemple, ```NounoursDBEntities``` __NE__ déclare __PAS__ de ```DbSet``` d'```Information``` (*toutefois, l'exemple fonctionnerait de la même manière si c'était le cas*).
|
||||
##### Quelques explications supplémentaires :
|
||||
* Même si ```NounoursDBEntities``` ne déclare pas de ```DbSet``` d'```Information```, **EntityFramework Core** détecte qu'il va falloir créer une table d'```Information``` lors de la migration.
|
||||
Nous pourrons nous en apercevoir plus tard dans ce document lorsque nous regarderons le résultat de la table.
|
||||
* Pour pouvoir relier une entité ```Nounours``` à une entité ```Information```, la propriété ```Information``` n'est pas suffisante. Il faut également une propriété dans la classe ```Nounours``` du type de l'identifiant de la classe ```Information``` pour faire le lien.
|
||||
Mais **EntityFramework Core** se charge de sa création lors de la migration. (*Vous pouvez l'ajouter vous-même si vous préférez*) Elle est l'équivalent de :
|
||||
```public int InformationId {get; set;}```
|
||||
mais n'a pas été écrite par le développeur : on parle alors de **shadow property**.
|
||||
|
||||
### La classe ```NounoursDBEntitiesWithStub```
|
||||
|
||||
* ```NounoursDBEntitiesWithStub``` est une classe fille de ```NounoursDBEntites```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
var info1 = new Information { InformationId = 1, MadeBy = "George Lucas", MadeIn = "Kashyyyk" };
|
||||
var info2 = new Information { InformationId = 2, MadeBy = "George Lucas", MadeIn = "Dagobah" };
|
||||
var info3 = new Information { InformationId = 3, MadeBy = "George Lucas", MadeIn = "Lune forestière d'Endor" };
|
||||
|
||||
modelBuilder.Entity<Information>().HasData(info1, info2, info3);
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property<int>("InformationId");
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new { UniqueId = Guid.Parse("{4422C524-B2CB-43EF-8263-990C3CEA7CAE}"), Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567, InformationId = 1 },
|
||||
new { UniqueId = Guid.Parse("{A4F84D92-C20F-4F2D-B3F9-CA00EF556E72}"), Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3, InformationId = 2 },
|
||||
new { UniqueId = Guid.Parse("{AE5FE535-F041-445E-B570-28B75BC78CB9}"), Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789, InformationId = 3 }
|
||||
);
|
||||
}
|
||||
```
|
||||
Elle crée d'abord des entités d'```Information```
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
var info1 = new Information { InformationId = 1, MadeBy = "George Lucas", MadeIn = "Kashyyyk" };
|
||||
var info2 = new Information { InformationId = 2, MadeBy = "George Lucas", MadeIn = "Dagobah" };
|
||||
var info3 = new Information { InformationId = 3, MadeBy = "George Lucas", MadeIn = "Lune forestière d'Endor" };
|
||||
```
|
||||
et les ajoute à la base :
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Information>().HasData(info1, info2, info3);
|
||||
```
|
||||
Elle va ensuite créer des instances de ```Nounours``` et les ajouter à la base, mais pour cela, il lui faut donner l'identifiant de l'```Information``` à lier au ```Nounours```.
|
||||
Nous sommes donc obligés dans ce cas de fournir la propriété ```InformationId``` dans ```Nounours``` pour pouvoir les lier.
|
||||
Deux solutions existent alors :
|
||||
1) soit vous rajoutez une propriété ```InformationId``` dans ```Nounours``` : mais c'est dommage d'avoir à le faire uniquement pour un stub.
|
||||
2) soit vous rajoutez la **shadow property** directement dans cette méthode ```OnModelCreating``` avec la ligne suivante qui est alors équivalente et permet de rajouter une propriété à une entité :
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Nounours>().Property<int>("InformationId");
|
||||
```
|
||||
On peut ensuite créer les instances de ```Nounours``` et les relier aux instances de ```Information``` en utilisant la nouvelle propriété ```InformationId``` de ```Nounours``` correspondant à la clé primaire de ``` Information```.
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new { UniqueId = Guid.Parse("{4422C524-B2CB-43EF-8263-990C3CEA7CAE}"), Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567, InformationId = 1 },
|
||||
new { UniqueId = Guid.Parse("{A4F84D92-C20F-4F2D-B3F9-CA00EF556E72}"), Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3, InformationId = 2 },
|
||||
new { UniqueId = Guid.Parse("{AE5FE535-F041-445E-B570-28B75BC78CB9}"), Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789, InformationId = 3 }
|
||||
);
|
||||
```
|
||||
|
||||
### La classe ```Program```
|
||||
* ```NounoursDBEntitiesWithStub``` est ensuite utilisée dans ```Program``` pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base :");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Information))
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Toutefois, si on ne précise pas qu'on veut également récupérer les contenus des propriétés ```Information``` des ```Nounours```, **EF Core** ne le fera pas et ne chargera que les propriétés simples des ```Nounours```.
|
||||
Pour forcer **EF Core** à charger également les données d'```Information```, on utilise la méthode d'extension ```Include``` :
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.Include(n => n.Information)
|
||||
```
|
||||
```db.NounoursSet``` récupère les données de la table de ```Nounours``` et ```.Include(n => n.Information)``` permet de préciser qu'on veut également récupérer les entités d'```Information``` associées à ces ```Nounours```.
|
||||
* La suite de l'exemple ajoute un nouveau ```Nounours``` et affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.Add(new Nounours { Nom = "Porg", DateDeNaissance = new DateTime(2017, 07, 19), NbPoils = 123, Information = new Information { MadeIn = "Ahch-To", MadeBy = "Jake Lunt Davies" } });
|
||||
db.SaveChangesAsync();
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base :");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Information))
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_010_SinglePropertyNavigation_conventions
|
||||
```
|
||||
|
||||
:::note rappel
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_010 --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple [**ex_042_010_SinglePropertyNavigation_conventions ➚**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_010_SinglePropertyNavigation_conventions).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à :
|
||||
```
|
||||
Contenu de la base :
|
||||
4422c524-b2cb-43ef-8263-990c3cea7cae: Chewbacca (27/05/1977, 1234567 poils, fait à Kashyyyk par George Lucas)
|
||||
a4f84d92-c20f-4f2d-b3f9-ca00ef556e72: Yoda (21/05/1980, 3 poils, fait à Dagobah par George Lucas)
|
||||
ae5fe535-f041-445e-b570-28b75bc78cb9: Ewok (25/05/1983, 3456789 poils, fait à Lune forestière d'Endor par George Lucas)
|
||||
|
||||
Ajout d'un nouveau nounours...
|
||||
|
||||
Contenu de la base :
|
||||
4422c524-b2cb-43ef-8263-990c3cea7cae: Chewbacca (27/05/1977, 1234567 poils, fait à Kashyyyk par George Lucas)
|
||||
a4f84d92-c20f-4f2d-b3f9-ca00ef556e72: Yoda (21/05/1980, 3 poils, fait à Dagobah par George Lucas)
|
||||
ae5fe535-f041-445e-b570-28b75bc78cb9: Ewok (25/05/1983, 3456789 poils, fait à Lune forestière d'Endor par George Lucas)
|
||||
dc53d93d-e15a-458d-94c9-bb3a61063377: Porg (19/07/2017, 123 poils, fait à Ahch-To par Jake Lunt Davies)
|
||||
```
|
||||
|
||||
:::note
|
||||
L'identifiant du dernier ```Nounours``` sera vraisemblablement différent puisqu'il est créé par la base lors de l'insertion.
|
||||
:::
|
||||
|
||||
## 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 ```NounoursDBEntitiesWithStub``` :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_010 --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.
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_010_SinglePropertyNavigation_conventions.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_010_SinglePropertyNavigation_conventions.csproj*.
|
||||

|
||||
* Vous pouvez remarquer que, même si ```NounoursDBEntites``` ne déclare pas de ```DbSet<Information>```, une table d'```Information``` a bien été créée.
|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 116 KiB |
@ -1,251 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.2. Single Property Navigation with Fluent API'
|
||||
sidebar_position: 3
|
||||
description: "explique comment relier simplement deux entités avec les annotations de données"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# Single Property Navigation with Fluent API
|
||||
|
||||
*19/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_011_SinglePropertyNavigation_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment réaliser une navigation entre deux entités avec une propriété simple (sens unique) avec Entity Framework Core et la *Fluent API*.
|
||||
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
|
||||
[**2.10.1. Single Property Navigation with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_01_SinglePropertyNavigation_conventions/ReadMe.md)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Nounours```
|
||||
* ```Information```
|
||||
* ```NounoursDBEntities```
|
||||
* ```NounoursDBEntitiesWithStub```
|
||||
|
||||
### Les classes entités : ```Nounours``` et ```Information```
|
||||
|
||||
Un ```Nounours``` contient différentes propriétés (```Nom```, ```DateDeNaissance```, ```NbPoils```) dont ```Information``` de type ```Information```.
|
||||
Une ```Information``` possède deux propriétés de type ```string```, mais aucun lien vers ```Nounours```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Nounours{
|
||||
+UniqueId: Guid
|
||||
+Nom: string
|
||||
+DateDeNaissance: DateTime
|
||||
+NbPoils: int
|
||||
}
|
||||
class Information{
|
||||
+InformationId: int
|
||||
+MadeBy: string
|
||||
+MadeIn: string
|
||||
}
|
||||
Nounours--> "1" Information : +Information
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Nounours``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _data annotation_).
|
||||
* ```Information``` possède un identifiant unique (qui peut être utilisé par convention d'écriture ou _data annotation_).
|
||||
* ```Nounours``` possède une association vers ```Information```
|
||||
```csharp title='Nounours.cs'
|
||||
public Information Information
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
* ```Information``` __NE__ possède __PAS__ d'association vers ```Nounours```
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```NounoursDBEntities``` dérive de ```DbContext```.
|
||||
* ```NounoursDBEntities``` déclare un seul ```DbSet``` de ```Nounours```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
```
|
||||
* Dans cet exemple, ```NounoursDBEntities``` __NE__ déclare __PAS__ de ```DbSet``` d'```Information``` (*toutefois, l'exemple fonctionnerait de la même manière si c'était le cas*).
|
||||
* La méthode ```OnModelCreating``` est réécrite et écrasera donc une partie des *conventions d'écriture* ou des *annotations de données*. Ici, par exemple, elle donne quelques indications sur la classe ```Nounours``` comme nous l'avons déjà vu dans les exemples précédents :
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date");
|
||||
|
||||
//...
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
```
|
||||
* Puis, ce qui nous intéresse plus particulièrement ici, elle définit la relation entre ```Nounours``` et ```Information``` : une entité ```Nounours``` possède une entité ```Information``` sans navigation inverse.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().HasOne(n => n.Information);
|
||||
```
|
||||
##### Quelques explications supplémentaires :
|
||||
* Même si ```NounoursDBEntities``` ne déclare pas de ```DbSet``` d'```Information```, **EntityFramework Core** détecte qu'il va falloir créer une table d'```Information``` lors de la migration.
|
||||
Nous pourrons nous en apercevoir plus tard dans ce document lorsque nous regarderons le résultat de la table.
|
||||
* Pour pouvoir relier une entité ```Nounours``` à une entité ```Information```, la propriété ```Information``` n'est pas suffisante. Il faut également une propriété dans la classe ```Nounours``` du type de l'identifiant de la classe ```Information``` pour faire le lien.
|
||||
Mais **EntityFramework Core** se charge de sa création lors de la migration. (*Vous pouvez l'ajouter vous-même si vous préférez*) Elle est l'équivalent de :
|
||||
```public int InformationId {get; set;}```
|
||||
mais n'a pas été écrite par le développeur : on parle alors de **shadow property**.
|
||||
|
||||
### La classe ```NounoursDBEntitiesWithStub```
|
||||
|
||||
* ```NounoursDBEntitiesWithStub``` est une classe fille de ```NounoursDBEntites```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
var info1 = new Information { InformationId = 1, MadeBy = "George Lucas", MadeIn = "Kashyyyk" };
|
||||
var info2 = new Information { InformationId = 2, MadeBy = "George Lucas", MadeIn = "Dagobah" };
|
||||
var info3 = new Information { InformationId = 3, MadeBy = "George Lucas", MadeIn = "Lune forestière d'Endor" };
|
||||
|
||||
modelBuilder.Entity<Information>().HasData(info1, info2, info3);
|
||||
|
||||
modelBuilder.Entity<Nounours>().Property<int>("InformationId");
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new { UniqueId = Guid.Parse("{4422C524-B2CB-43EF-8263-990C3CEA7CAE}"), Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567, InformationId = 1 },
|
||||
new { UniqueId = Guid.Parse("{A4F84D92-C20F-4F2D-B3F9-CA00EF556E72}"), Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3, InformationId = 2 },
|
||||
new { UniqueId = Guid.Parse("{AE5FE535-F041-445E-B570-28B75BC78CB9}"), Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789, InformationId = 3 }
|
||||
);
|
||||
}
|
||||
```
|
||||
Elle crée d'abord des entités d'```Information```
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
var info1 = new Information { InformationId = 1, MadeBy = "George Lucas", MadeIn = "Kashyyyk" };
|
||||
var info2 = new Information { InformationId = 2, MadeBy = "George Lucas", MadeIn = "Dagobah" };
|
||||
var info3 = new Information { InformationId = 3, MadeBy = "George Lucas", MadeIn = "Lune forestière d'Endor" };
|
||||
```
|
||||
et les ajoute à la base :
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Information>().HasData(info1, info2, info3);
|
||||
```
|
||||
Elle va ensuite créer des instances de ```Nounours``` et les ajouter à la base, mais pour cela, il lui faut donner l'identifiant de l'```Information``` à lier au ```Nounours```.
|
||||
Nous sommes donc obligés dans ce cas de fournir la propriété ```InformationId``` dans ```Nounours``` pour pouvoir les lier.
|
||||
Deux solutions existent alors :
|
||||
1) soit vous rajoutez une propriété ```InformationId``` dans ```Nounours``` : mais c'est dommage d'avoir à le faire uniquement pour un stub.
|
||||
2) soit vous rajoutez la **shadow property** directement dans cette méthode ```OnModelCreating``` avec la ligne suivante qui est alors équivalente et permet de rajouter une propriété à une entité :
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Nounours>().Property<int>("InformationId");
|
||||
```
|
||||
On peut ensuite créer les instances de ```Nounours``` et les relier aux instances de ```Information``` en utilisant la nouvelle propriété ```InformationId``` de ```Nounours``` correspondant à la clé primaire de ``` Information```.
|
||||
```csharp title='NounoursDBEntitiesWithStub.cs'
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new { UniqueId = Guid.Parse("{4422C524-B2CB-43EF-8263-990C3CEA7CAE}"), Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567, InformationId = 1 },
|
||||
new { UniqueId = Guid.Parse("{A4F84D92-C20F-4F2D-B3F9-CA00EF556E72}"), Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3, InformationId = 2 },
|
||||
new { UniqueId = Guid.Parse("{AE5FE535-F041-445E-B570-28B75BC78CB9}"), Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789, InformationId = 3 }
|
||||
);
|
||||
```
|
||||
|
||||
### La classe ```Program```
|
||||
* ```NounoursDBEntitiesWithStub``` est ensuite utilisée dans ```Program``` pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base :");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Information))
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Toutefois, si on ne précise pas qu'on veut également récupérer les contenus des propriétés ```Information``` des ```Nounours```, **EF Core** ne le fera et ne chargera que les propriétés simples des ```Nounours```.
|
||||
Pour forcer **EF Core** à charger également les données d'```Information```, on utilise la méthode d'extension ```Include``` :
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.Include(n => n.Information)
|
||||
```
|
||||
```db.NounoursSet``` récupère les données de la table de ```Nounours``` et ```.Include(n => n.Information)``` permet de préciser qu'on veut également récupérer les entités d'```Information``` associées à ces ```Nounours```.
|
||||
* La suite de l'exemple ajoute un nouveau ```Nounours``` et affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
db.NounoursSet.Add(new Nounours { Nom = "Porg", DateDeNaissance = new DateTime(2017, 07, 19), NbPoils = 123, Information = new Information { MadeIn = "Ahch-To", MadeBy = "Jake Lunt Davies" } });
|
||||
db.SaveChangesAsync();
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base :");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Information))
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_011_SinglePropertyNavigation_FluentAPI
|
||||
```
|
||||
:::note Rappel
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_011 --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update --context NounoursDBEntitiesWithStub
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple [**ex_042_011_SinglePropertyNavigation_FluentAPI ➚**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_011_SinglePropertyNavigation_FluentAPI).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à :
|
||||
|
||||
```
|
||||
Contenu de la base :
|
||||
4422c524-b2cb-43ef-8263-990c3cea7cae: Chewbacca (27/05/1977, 1234567 poils, fait à Kashyyyk par George Lucas)
|
||||
a4f84d92-c20f-4f2d-b3f9-ca00ef556e72: Yoda (21/05/1980, 3 poils, fait à Dagobah par George Lucas)
|
||||
ae5fe535-f041-445e-b570-28b75bc78cb9: Ewok (25/05/1983, 3456789 poils, fait à Lune forestière d'Endor par George Lucas)
|
||||
|
||||
Ajout d'un nouveau nounours...
|
||||
|
||||
Contenu de la base :
|
||||
4422c524-b2cb-43ef-8263-990c3cea7cae: Chewbacca (27/05/1977, 1234567 poils, fait à Kashyyyk par George Lucas)
|
||||
a4f84d92-c20f-4f2d-b3f9-ca00ef556e72: Yoda (21/05/1980, 3 poils, fait à Dagobah par George Lucas)
|
||||
ae5fe535-f041-445e-b570-28b75bc78cb9: Ewok (25/05/1983, 3456789 poils, fait à Lune forestière d'Endor par George Lucas)
|
||||
dc53d93d-e15a-458d-94c9-bb3a61063377: Porg (19/07/2017, 123 poils, fait à Ahch-To par Jake Lunt Davies)
|
||||
```
|
||||
|
||||
:::note
|
||||
L'identifiant du dernier ```Nounours``` sera vraisemblablement différent puisqu'il est créé par la base lors de l'insertion.
|
||||
:::
|
||||
|
||||
## 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 ```NounoursDBEntitiesWithStub``` :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_010 --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.
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_011_SinglePropertyNavigation_FluentAPI.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_011_SinglePropertyNavigation_FluentAPI.csproj*.
|
||||

|
||||
* Vous pouvez remarquer que, même si ```NounoursDBEntites``` ne déclare pas de ```DbSet<Information>```, une table d'```Information``` a bien été créée.
|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
@ -1,243 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.3. One to One with data annotations'
|
||||
sidebar_position: 4
|
||||
description: "explique comment préparer un One to One avec les annotations de données"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# One to One relationships with data annotations
|
||||
|
||||
*20/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_012_OneToOne_conventions)
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
[**2.10.4. One To One with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_04_OneToOne_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Nounours```
|
||||
* ```CarnetDeSante```
|
||||
* ```NounoursDBEntities```
|
||||
* ```StubbedContext```
|
||||
|
||||
### Les classes entités : ```Nounours``` et ```CarnetDeSante```
|
||||
|
||||
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```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Nounours{
|
||||
+UniqueId: int
|
||||
+Nom: string
|
||||
+DateDeNaissance: DateTime
|
||||
+NbPoils: int
|
||||
}
|
||||
class CarnetDeSante{
|
||||
+UniqueId: int
|
||||
+LastModified: DateTime
|
||||
}
|
||||
Nounours "1" -- "1" CarnetDeSante : +Owner +Carnet
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Nounours``` possède une association vers ```CarnetDeSante```
|
||||
```csharp title='Nounours.cs'
|
||||
public CarnetDeSante Carnet { get; set; }
|
||||
```
|
||||
* ```CarnetDeSante``` possède une association vers ```Nounours```
|
||||
```csharp title='CarnetDeSante.cs'
|
||||
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 title='CarnetDeSante.cs'
|
||||
[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```.
|
||||
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```NounoursDBEntities``` dérive de ```DbContext```.
|
||||
* ```NounoursDBEntities``` déclare deux ```DbSet``` : un de ```Nounours``` et l'autre de ```CarnetDeSante```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
public DbSet<CarnetDeSante> Carnets { get; set; }
|
||||
```
|
||||
##### Quelques explications supplémentaires :
|
||||
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.
|
||||
|
||||
### La classe ```StubbedContext```
|
||||
|
||||
* ```StubbedContext``` est une classe fille de ```NounoursDBEntities```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='StubbedContext.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<CarnetDeSante>().HasData(
|
||||
new CarnetDeSante { UniqueId=1, LastModified = DateTime.Today },
|
||||
new CarnetDeSante { UniqueId=2, LastModified = new DateTime(1980, 5, 21) },
|
||||
new CarnetDeSante { UniqueId=3, LastModified = new DateTime(1983, 5, 25) }
|
||||
);
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new Nounours { UniqueId=1, Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 },
|
||||
new Nounours { UniqueId=2, Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 },
|
||||
new Nounours { UniqueId=3, Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }
|
||||
);
|
||||
}
|
||||
```
|
||||
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés ```Owner``` de ```CarnetDeSante``` et ```Carnet``` de ```Nounours```.
|
||||
Le simple fait d'utiliser la même clé (propriétés ```UniqueId```) est suffisant puisque celle de ```CarnetDeSante``` est une clé étrangère.
|
||||
* Notez que ce ne sera pas le cas lors d'une utilisation *classique* de nos classes (ajout, modification...). Nous ne donnerons plus les identifiants directement mais les références des propriétés ```Carnet``` et ```Owner```.
|
||||
|
||||
### La classe ```Program```
|
||||
* La classe ```StubbedContext``` est ensuite indirectement utilisée dans ```Program``` pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire : ceci est fait lors de la migration et la création de la base.
|
||||
* On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes données si la migration est faite à partir de ```NounoursDBEntites```) ou le stub (si la migration est faite à partir de ```StubbedContext```).
|
||||
:::info Include
|
||||
Notez l'utilisation d'```Include``` dans ```db.NounoursSet.Include(n => n.Carnet)``` sinon, les ```CarnetDeSante``` ne sont pas chargés. ```Include``` n'est pas utilisé ensuite dans ```db.Carnets``` car les ```Nounours``` ont déjà été chargés depuis la connexion. Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'abord ```db.Carnets.Include(c => c.Owner)``` puis simplement ```db.NounoursSet```.
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base (nounours) : ");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
|
||||
{
|
||||
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
|
||||
}
|
||||
|
||||
WriteLine("Contenu de la base (carnets de santé) : ");
|
||||
foreach (var c in db.Carnets)
|
||||
{
|
||||
WriteLine($"\t{c}");
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
* La suite de l'exemple ajoute un nouveau ```Nounours``` et son ```CarnetDeSante``` puis affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
|
||||
WriteLine("\nAjout d'un nounours et de son carnet de santé\n");
|
||||
|
||||
Nounours porg = new Nounours { Nom = "Porg", DateDeNaissance = new DateTime(2017, 7, 19), NbPoils = 123 };
|
||||
CarnetDeSante carnetPorg = new CarnetDeSante { LastModified = DateTime.Now, Owner = porg };
|
||||
porg.Carnet = carnetPorg;
|
||||
|
||||
db.AddRange(porg, carnetPorg);
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base (nounours) : ");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
|
||||
{
|
||||
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
|
||||
}
|
||||
|
||||
WriteLine("Contenu de la base (carnets de santé) : ");
|
||||
foreach (var c in db.Carnets)
|
||||
{
|
||||
WriteLine($"\t{c}");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* 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
|
||||
```
|
||||
:::note Note
|
||||
Si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add ex_042_012 --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 ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_012_OneToOne_conventions).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à :
|
||||
```
|
||||
Contenu de la base (nounours) :
|
||||
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
|
||||
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
|
||||
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
|
||||
Contenu de la base (carnets de santé) :
|
||||
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
|
||||
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
|
||||
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
|
||||
|
||||
Ajout d'un nounours et de son carnet de santé
|
||||
|
||||
Contenu de la base (nounours) :
|
||||
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
|
||||
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
|
||||
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
|
||||
4: Porg (19/07/2017, 123 poils), LastModified: 20/01/2020
|
||||
Contenu de la base (carnets de santé) :
|
||||
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
|
||||
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
|
||||
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
|
||||
4 : carnet de Porg, modifié la dernière fois le 20/01/2020
|
||||
```
|
||||
:::note Note
|
||||
L'identifiant du dernier ```Nounours``` sera vraisemblablement différent puisqu'il est créé par la base lors de l'insertion.
|
||||
:::
|
||||
|
||||
## 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 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.
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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*.
|
||||

|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 102 KiB |
@ -1,304 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.4. One to One with Fluent API'
|
||||
sidebar_position: 5
|
||||
description: "explique comment préparer un One to One avec la Fluent API"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# One to One relationships with Fluent API
|
||||
|
||||
*20/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/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
|
||||
[**2.10.3. One To One with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_03_OneToOne_dataAnnotations/)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Nounours```
|
||||
* ```CarnetDeSante```
|
||||
* ```NounoursDBEntities```
|
||||
* ```StubbedContext```
|
||||
|
||||
### Les classes entités : ```Nounours``` et ```CarnetDeSante```
|
||||
|
||||
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```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Nounours{
|
||||
+UniqueId: int
|
||||
+Nom: string
|
||||
+DateDeNaissance: DateTime
|
||||
+NbPoils: int
|
||||
}
|
||||
class CarnetDeSante{
|
||||
+UniqueId: int
|
||||
+LastModified: DateTime
|
||||
}
|
||||
Nounours "1" -- "1" CarnetDeSante : +Owner +Carnet
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Nounours``` possède une association vers ```CarnetDeSante```
|
||||
```csharp title='Nounours.cs'
|
||||
public CarnetDeSante Carnet { get; set; }
|
||||
```
|
||||
* ```CarnetDeSante``` possède une association vers ```Nounours```
|
||||
```csharp title='CarnetDeSante.cs'
|
||||
public Nounours Owner { get; set; }
|
||||
```
|
||||
* ```Nounours``` possède un identifiant unique ```UniqueId```.
|
||||
* ```CarnetDeSante``` possède un identifiant unique ```UniqueId```.
|
||||
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```NounoursDBEntities``` dérive de ```DbContext```.
|
||||
* ```NounoursDBEntities``` déclare deux ```DbSet``` : un de ```Nounours``` et l'autre de ```CarnetDeSante```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
public DbSet<Nounours> NounoursSet { get; set; }
|
||||
public DbSet<CarnetDeSante> Carnets { get; set; }
|
||||
```
|
||||
* La classe réécrit ensuite la méthode ```OnModelCreating``` :
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
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 title='NounoursDBEntities.cs'
|
||||
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
|
||||
```
|
||||
* la définition de la clé primaire :
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
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 title='NounoursDBEntities.cs'
|
||||
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 title='NounoursDBEntities.cs'
|
||||
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 title='NounoursDBEntities.cs'
|
||||
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 title='NounoursDBEntities.cs'
|
||||
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 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```
|
||||
|
||||
* ```StubbedContext``` est une classe fille de ```NounoursDBEntities```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='StubbedContext.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<CarnetDeSante>().HasData(
|
||||
new CarnetDeSante { UniqueId=1, LastModified = DateTime.Today },
|
||||
new CarnetDeSante { UniqueId=2, LastModified = new DateTime(1980, 5, 21) },
|
||||
new CarnetDeSante { UniqueId=3, LastModified = new DateTime(1983, 5, 25) }
|
||||
);
|
||||
|
||||
modelBuilder.Entity<Nounours>().HasData(
|
||||
new Nounours { UniqueId=1, Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 },
|
||||
new Nounours { UniqueId=2, Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 },
|
||||
new Nounours { UniqueId=3, Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }
|
||||
);
|
||||
}
|
||||
```
|
||||
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés ```Owner``` de ```CarnetDeSante``` et ```Carnet``` de ```Nounours```.
|
||||
Le simple fait d'utiliser la même clé (propriétés ```UniqueId```) est suffisant puisque celle de ```CarnetDeSante``` est une clé étrangère.
|
||||
* Notez que ce ne sera pas le cas lors d'une utilisation *classique* de nos classes (ajout, modification...). Nous ne donnerons plus les identifiants directement mais les références des propriétés ```Carnet``` et ```Owner```.
|
||||
|
||||
### La classe ```Program```
|
||||
* La classe ```StubbedContext``` est ensuite indirectement utilisée dans ```Program``` pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire : ceci est fait lors de la migration et la création de la base.
|
||||
* On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes données si la migration est faite à partir de ```NounoursDBEntites```) ou le stub (si la migration est faite à partir de ```StubbedContext```).
|
||||
:::note Include
|
||||
Notez l'utilisation d'```Include``` dans ```db.NounoursSet.Include(n => n.Carnet)``` sinon, les ```CarnetDeSante``` ne sont pas chargés. ```Include``` n'est pas utilisé ensuite dans ```db.Carnets``` car les ```Nounours``` ont déjà été chargés depuis la connexion. Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'abord ```db.Carnets.Include(c => c.Owner)``` puis simplement ```db.NounoursSet```.
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base (nounours) : ");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
|
||||
{
|
||||
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
|
||||
}
|
||||
|
||||
WriteLine("Contenu de la base (carnets de santé) : ");
|
||||
foreach (var c in db.Carnets)
|
||||
{
|
||||
WriteLine($"\t{c}");
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
* La suite de l'exemple ajoute un nouveau ```Nounours``` et son ```CarnetDeSante``` puis affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
|
||||
WriteLine("\nAjout d'un nounours et de son carnet de santé\n");
|
||||
|
||||
Nounours porg = new Nounours { Nom = "Porg", DateDeNaissance = new DateTime(2017, 7, 19), NbPoils = 123 };
|
||||
CarnetDeSante carnetPorg = new CarnetDeSante { LastModified = DateTime.Now, Owner = porg };
|
||||
porg.Carnet = carnetPorg;
|
||||
|
||||
db.AddRange(porg, carnetPorg);
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
WriteLine("Contenu de la base (nounours) : ");
|
||||
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
|
||||
{
|
||||
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
|
||||
}
|
||||
|
||||
WriteLine("Contenu de la base (carnets de santé) : ");
|
||||
foreach (var c in db.Carnets)
|
||||
{
|
||||
WriteLine($"\t{c}");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* 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_013_OneToOne_FluentAPI
|
||||
```
|
||||
:::note Note
|
||||
Si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
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_013_OneToOne_FluentAPI ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_013_OneToOne_FluentAPI).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à :
|
||||
```
|
||||
Contenu de la base (nounours) :
|
||||
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
|
||||
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
|
||||
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
|
||||
Contenu de la base (carnets de santé) :
|
||||
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
|
||||
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
|
||||
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
|
||||
|
||||
Ajout d'un nounours et de son carnet de santé
|
||||
|
||||
Contenu de la base (nounours) :
|
||||
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
|
||||
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
|
||||
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
|
||||
4: Porg (19/07/2017, 123 poils), LastModified: 20/01/2020
|
||||
Contenu de la base (carnets de santé) :
|
||||
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
|
||||
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
|
||||
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
|
||||
4 : carnet de Porg, modifié la dernière fois le 20/01/2020
|
||||
```
|
||||
:::note Note
|
||||
L'identifiant du dernier ```Nounours``` sera vraisemblablement différent puisqu'il est créé par la base lors de l'insertion.
|
||||
:::
|
||||
|
||||
## 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_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.
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_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_013_OneToOne_FluentAPI.csproj*.
|
||||

|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
@ -1,375 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.5. One to Many with data annotations'
|
||||
sidebar_position: 6
|
||||
description: "explique comment préparer un One to Many avec les annotations de données"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# One To Many with data annotations
|
||||
|
||||
*21/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment réaliser une relation *One To Many* entre deux entités avec *Entity Framework Core* et l'*annotation de données*.
|
||||
Une version équivalente réalisée avec les *conventions d'écriture* et l'établissement d'une clé étrangère de manière implicite est disponible dans l'exemple
|
||||
[**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions)
|
||||
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
|
||||
[**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Album```
|
||||
* ```Morceau```
|
||||
* ```AlbumDBEntities```
|
||||
* ```StubbedContext```
|
||||
|
||||
### Les classes entités : ```Album``` et ```Morceau```
|
||||
|
||||
La relation **One To Many** est mise en évidence de la manière suivante : un ```Album``` possède plusieurs ```Morceau```,
|
||||
et un ```Morceau``` possède un ```Album```.
|
||||
Un ```Album``` contient différentes propriétés (```Titre```, ```DateDeSortie```) dont ```Morceaux``` de type ```ICollection<Morceau>```.
|
||||
Un ```Morceau``` possède une propriété de type ```string``` (```Titre```), et une propriété ```Album``` de type ```Album```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Album``` possède une association vers ```Morceau```
|
||||
```csharp title='Album.cs'
|
||||
public ICollection<Morceau> Morceaux { get; set; } = new List<Morceau>();
|
||||
```
|
||||
* ```Morceau``` possède une association vers ```Album```
|
||||
```csharp title='Morceau.cs'
|
||||
public Album Album { get; set; }
|
||||
```
|
||||
* ```Album``` possède un identifiant unique ```AlbumId``` (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)]```.
|
||||
* ```Morceau``` possède un identifiant unique ```MorceauId``` (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)]```.
|
||||
* Le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau``` est lié.
|
||||
Dans le cas de l'utilisation des *conventions d'écriture* et des *annotations de données*, ceci peut se faire explicitement ou implicitement. Cet exemple, le fait de manière explicite avec les *annotations de données*. Mais il est possible
|
||||
de le faire de manière implicite en utilisant les *conventions d'écriture* (cf. [**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions))
|
||||
ou de manière explicite en utilisant la *Fluent API* (cf. [**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI)). Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
|
||||
Ici, on ajoute donc une nouvelle propriété à ```Morceau``` qui va permettre de stocker l'identifiant unique de ```Album``` (cette propriété doit donc être du même type que la clé primaire de ```Album```) :
|
||||
```csharp title='Morceau.cs'
|
||||
public int AlbumForeignKey
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
puis on annote la propriété ```Album``` pour préciser que cette relation se base sur la clé étrangère définie plus haut :
|
||||
```csharp title='Morceau.cs'
|
||||
[ForeignKey("AlbumForeignKey")]
|
||||
public Album Album
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
```
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
+AlbumForeignKey: int
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
:::caution IMPORTANT
|
||||
dans ```Album``` : ```ForeignKey: AlbumForeignKey```
|
||||
:::
|
||||
|
||||
|
||||
### La classe ```AlbumDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```AlbumDBEntities``` dérive de ```DbContext```.
|
||||
* ```AlbumDBEntities``` déclare deux ```DbSet``` : un de ```Album``` et l'autre de ```Morceau```.
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
public DbSet<Album> Albums { get; set; }
|
||||
public DbSet<Morceau> Morceaux { get; set; }
|
||||
```
|
||||
##### Quelques explications supplémentaires :
|
||||
Comme dit plus haut, le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau``` est lié.
|
||||
Dans le cas de l'utilisation des *conventions d'écriture* et des *annotations de données*, ceci peut se faire explicitement ou implicitement. Cet exemple, le fait de manière explicite avec les *annotations de données*. Mais il est possible
|
||||
de le faire de manière implicite en utilisant les *conventions d'écriture* (cf. [**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions))
|
||||
ou de manière explicite en utilisant la *Fluent API* (cf. [**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI)).
|
||||
|
||||
### La classe ```StubbedContext```
|
||||
|
||||
* ```StubbedContext``` est une classe fille de ```AlbumDBEntities```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='StubbedContext.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
Album kindofblue = new Album { AlbumId=1, Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
|
||||
Album dialogue = new Album { AlbumId=2, Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
|
||||
|
||||
modelBuilder.Entity<Album>().HasData(kindofblue, dialogue);
|
||||
|
||||
modelBuilder.Entity<Morceau>().HasData(new Morceau { MorceauId = 1, AlbumForeignKey = 1, Titre = "So What" },
|
||||
new Morceau { MorceauId = 2, AlbumForeignKey = 1, Titre = "Freddie Freeloader" },
|
||||
new Morceau { MorceauId = 3, AlbumForeignKey = 1, Titre = "Blue in Green" },
|
||||
new Morceau { MorceauId = 4, AlbumForeignKey = 1, Titre = "All Blues" },
|
||||
new Morceau { MorceauId = 5, AlbumForeignKey = 1, Titre = "Flamenco Sketches" },
|
||||
new Morceau { MorceauId = 6, AlbumForeignKey = 2, Titre = "Catta" },
|
||||
new Morceau { MorceauId = 7, AlbumForeignKey = 2, Titre = "Idle While" },
|
||||
new Morceau { MorceauId = 8, AlbumForeignKey = 2, Titre = "Les Noirs Marchant" },
|
||||
new Morceau { MorceauId = 9, AlbumForeignKey = 2, Titre = "Dialogue" },
|
||||
new Morceau { MorceauId = 10, AlbumForeignKey = 2, Titre = "Ghetto Lights" },
|
||||
new Morceau { MorceauId = 11, AlbumForeignKey = 2, Titre = "Jasper" } );
|
||||
}
|
||||
```
|
||||
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés ```Album``` de ```Morceau``` et ```Morceaux``` de ```Album```.
|
||||
Le simple fait d'utiliser la clé étrangère (propriété ```AlbumForeignKey```) dans ```Morceau``` est suffisant.
|
||||
* Notez que ce ne sera pas le cas lors d'une utilisation *classique* de nos classes (ajout, modification...). Nous ne donnerons plus les identifiants directement mais les références des propriétés ```Morceaux``` et ```Album```.
|
||||
|
||||
### La classe ```Program```
|
||||
* On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes données si la migration est faite à partir de ```AlbumDBEntites```) ou le stub (si la migration est faite à partir de ```StubbedContext```).
|
||||
:::note Include
|
||||
Notez l'utilisation d'```Include``` dans ```db.Albums.Include(a => a.Morceaux)``` sinon, les ```Morceau``` ne sont pas chargés.
|
||||
```Include``` n'est pas utilisé ensuite dans ```db.Morceaux``` car les ```Album``` ont déjà été chargés depuis la connexion.
|
||||
Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'abord ```db.Morceaux.Include(m => m.Album)``` puis simplement ```db.Albums```.
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
* La suite de l'exemple ajoute un nouvel ```Album``` et ses ```Morceau``` puis affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
//...
|
||||
|
||||
WriteLine("\nAjout d'un album et 6 morceaux...\n");
|
||||
|
||||
Album captainMarvel = new Album { Titre = "Captain Marvel", DateDeSortie = new DateTime(1972, 3, 3) };
|
||||
Morceau[] morceaux = { new Morceau { Titre = "La Fiesta", Album = captainMarvel },
|
||||
new Morceau { Titre = "Five Hundred Miles High", Album = captainMarvel },
|
||||
new Morceau { Titre = "Captain Marvel", Album = captainMarvel },
|
||||
new Morceau { Titre = "Time's Lie", Album = captainMarvel },
|
||||
new Morceau { Titre = "Lush Life", Album = captainMarvel },
|
||||
new Morceau { Titre = "Day Waves", Album = captainMarvel }
|
||||
};
|
||||
foreach (var m in morceaux)
|
||||
{
|
||||
captainMarvel.Morceaux.Add(m);
|
||||
}
|
||||
|
||||
db.Add(captainMarvel);
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package* sous Windows (pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*) ou le *Terminal* sous MacOSX.
|
||||
* 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_014_OneToMany_dataAnnotations
|
||||
```
|
||||
:::note Note
|
||||
Si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration : vous devez préciser la classe fille de ```DbContext``` à utiliser : soit ```AlbumDBEntities```, soit ```StubbedContext```.
|
||||
```
|
||||
dotnet ef migrations add ex_042_014 --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_014_OneToMany_dataAnnotations ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à (si vous avez utilisé ```StubbedContext```) :
|
||||
```
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
3: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
12: La Fiesta (album : Captain Marvel)
|
||||
13: Five Hundred Miles High (album : Captain Marvel)
|
||||
14: Captain Marvel (album : Captain Marvel)
|
||||
15: Time's Lie (album : Captain Marvel)
|
||||
16: Lush Life (album : Captain Marvel)
|
||||
17: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## 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 ```AlbumDBEntities``` à la place de ```StubbedContext``` :
|
||||
```
|
||||
dotnet ef migrations add ex_042_014 --context AlbumDBEntities
|
||||
dotnet ef database update --context AlbumDBEntities
|
||||
```
|
||||
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les 2 albums du stub.
|
||||
Il pourra ressembler à :
|
||||
```
|
||||
Albums :
|
||||
|
||||
Morceaux :
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: La Fiesta (album : Captain Marvel)
|
||||
2: Five Hundred Miles High (album : Captain Marvel)
|
||||
3: Captain Marvel (album : Captain Marvel)
|
||||
4: Time's Lie (album : Captain Marvel)
|
||||
5: Lush Life (album : Captain Marvel)
|
||||
6: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_014_OneToMany_dataAnnotations.Albums.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_014_OneToMany_dataAnnotations.csproj*.
|
||||

|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 117 KiB |
@ -1,424 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.6. One to Many with naming conventions'
|
||||
sidebar_position: 7
|
||||
description: "explique comment préparer un One to Many avec les conventions de nommage"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# One To Many with naming conventions
|
||||
|
||||
*22/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment réaliser une relation *One To Many* entre deux entités
|
||||
avec *Entity Framework Core* et les *conventions d'écriture* avec génération
|
||||
implicite d'une clé étrangère.
|
||||
Une version équivalente réalisée avec les *annotations de données* et
|
||||
l'établissement d'une clé étrangère de manière explicite est disponible dans
|
||||
l'exemple [**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations).
|
||||
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
|
||||
[**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI).
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Album```
|
||||
* ```Morceau```
|
||||
* ```AlbumDBEntities```
|
||||
* ```StubbedContext```
|
||||
|
||||
### Les classes entités : ```Album``` et ```Morceau```
|
||||
|
||||
La relation **One To Many** est mise en évidence de la manière suivante :
|
||||
un ```Album``` possède plusieurs ```Morceau```, et un ```Morceau``` possède
|
||||
un ```Album```.
|
||||
Un ```Album``` contient différentes propriétés (```Titre```, ```DateDeSortie```)
|
||||
dont ```Morceaux``` de type ```ICollection<Morceau>```.
|
||||
Un ```Morceau``` possède une propriété de type ```string``` (```Titre```), et
|
||||
une propriété ```Album``` de type ```Album```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Album``` possède une association vers ```Morceau```
|
||||
```csharp title='Album.cs'
|
||||
public ICollection<Morceau> Morceaux { get; set; } = new List<Morceau>();
|
||||
```
|
||||
* ```Morceau``` possède une association vers ```Album```
|
||||
```csharp title='Morceau.cs'
|
||||
public Album Album { get; set; }
|
||||
```
|
||||
* ```Album``` possède un identifiant unique ```AlbumId``` (qui par convention
|
||||
d'écriture, sera la clé primaire et sera généré par la base lors de l'insertion).
|
||||
* ```Morceau``` possède un identifiant unique ```MorceauId``` (qui par convention
|
||||
d'écriture, sera la clé primaire et sera généré par la base lors de l'insertion).
|
||||
* Le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table
|
||||
de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau```
|
||||
est lié.
|
||||
Dans le cas de l'utilisation des *conventions d'écriture*, ceci peut se faire
|
||||
explicitement ou implicitement. Cet exemple, le fait de manière implicite avec
|
||||
les *conventions d'écriture*. Mais il est possible de le faire de manière explicite
|
||||
en utilisant les *annotations de données* (cf. [**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations))
|
||||
ou de manière explicite en utilisant la *Fluent API* (cf. [**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI)).
|
||||
Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter
|
||||
cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
|
||||
Ici, on __N'__ajoute __PAS__ de nouvelle propriété à ```Morceau``` pour stocker
|
||||
l'identifiant unique de ```Album``` comme dans l'exemple [**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations).
|
||||
|
||||
### La classe ```AlbumDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```AlbumDBEntities``` dérive de ```DbContext```.
|
||||
* ```AlbumDBEntities``` déclare deux ```DbSet``` : un de ```Album``` et
|
||||
l'autre de ```Morceau```.
|
||||
```csharp
|
||||
public DbSet<Album> Albums { get; set; }
|
||||
public DbSet<Morceau> Morceaux { get; set; }
|
||||
```
|
||||
##### Quelques explications supplémentaires :
|
||||
Comme dit plus haut, le lien entre les deux tables va se faire avec l'ajout d'une
|
||||
colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album```
|
||||
auquel ce ```Morceau``` est lié.
|
||||
Dans le cas de l'utilisation des *conventions d'écriture* et des
|
||||
*annotations de données*, ceci peut se faire explicitement ou implicitement.
|
||||
Cet exemple, le fait de manière implicite avec les *conventions d'écriture*. Mais il
|
||||
est possible de le faire de manière explicite en utilisant les *annotations de données*
|
||||
(cf. [**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations))
|
||||
ou de manière explicite en utilisant la *Fluent API* (cf. [**2.10.7. One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI)).
|
||||
Ici, c'est *EF Core* qui ajoute automatiquement une propriété ```AlbumId```
|
||||
à ```Morceau``` : c'est la clé étrangère. Le nom de cette propriété est choisi à
|
||||
partir d'une combinaison du nom de la propriété allant de ```Morceau``` à ```Album``` (ici ```Album```),
|
||||
du type pointé par la clé étrangère (ici ```Album```), et de ```Id``` => ici *EF Core*
|
||||
a *choisi* ```AlbumId```.
|
||||
Tout ceci est fait automatiquement.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
==> +AlbumId: int <==
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
:::caution AlbumID
|
||||
Notez l'ajout automatique de ```AlbumId```
|
||||
|
||||
dans ```Album``` : ```ForeignKey: AlbumId```
|
||||
:::
|
||||
|
||||
Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter
|
||||
cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
|
||||
|
||||
### La classe ```StubbedContext```
|
||||
|
||||
* ```StubbedContext``` est une classe fille de ```AlbumDBEntities```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère.
|
||||
Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la
|
||||
méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis
|
||||
ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='StubbedContext.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
Album kindofblue = new Album { AlbumId=1, Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
|
||||
Album dialogue = new Album { AlbumId=2, Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
|
||||
|
||||
modelBuilder.Entity<Album>().HasData(kindofblue, dialogue);
|
||||
|
||||
modelBuilder.Entity<Morceau>().Property<int>("AlbumId");
|
||||
|
||||
modelBuilder.Entity<Morceau>().HasData(new { MorceauId = 1, AlbumId = 1, Titre = "So What" },
|
||||
new { MorceauId = 2, AlbumId = 1, Titre = "Freddie Freeloader" },
|
||||
new { MorceauId = 3, AlbumId = 1, Titre = "Blue in Green" },
|
||||
new { MorceauId = 4, AlbumId = 1, Titre = "All Blues" },
|
||||
new { MorceauId = 5, AlbumId = 1, Titre = "Flamenco Sketches" },
|
||||
new { MorceauId = 6, AlbumId = 2, Titre = "Catta" },
|
||||
new { MorceauId = 7, AlbumId = 2, Titre = "Idle While" },
|
||||
new { MorceauId = 8, AlbumId = 2, Titre = "Les Noirs Marchant" },
|
||||
new { MorceauId = 9, AlbumId = 2, Titre = "Dialogue" },
|
||||
new { MorceauId = 10, AlbumId = 2, Titre = "Ghetto Lights" },
|
||||
new { MorceauId = 11, AlbumId = 2, Titre = "Jasper" }
|
||||
);
|
||||
}
|
||||
```
|
||||
* __Attention toutefois__, ici, puisque nous devons donner la clé étrangère, il faut
|
||||
d'abord ajouter une colonne (une propriété) à notre entité ```Morceau```, qui permettra
|
||||
de stocker la clé étrangère pour pointer l'```Album``` associé :
|
||||
```csharp title='StubbedContext.cs'
|
||||
modelBuilder.Entity<Morceau>().Property<int>("AlbumId");
|
||||
```
|
||||
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés
|
||||
```Album``` de ```Morceau``` et ```Morceaux``` de ```Album```.
|
||||
Le simple fait d'utiliser la clé étrangère (propriété ```AlbumId```
|
||||
ajoutée précédemment) dans ```Morceau``` est suffisant.
|
||||
* Notez que ce ne sera pas le cas lors d'une utilisation *classique* de nos classes
|
||||
(ajout, modification...). Nous ne donnerons plus les identifiants directement mais
|
||||
les références des propriétés ```Morceaux``` et ```Album```.
|
||||
|
||||
### La classe ```Program```
|
||||
* On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes
|
||||
données si la migration est faite à partir de ```AlbumDBEntites```) ou le stub
|
||||
(si la migration est faite à partir de ```StubbedContext```).
|
||||
:::note Note
|
||||
Notez l'utilisation d'```Include``` dans
|
||||
```db.Albums.Include(a => a.Morceaux)``` sinon, les ```Morceau``` ne sont pas
|
||||
chargés.
|
||||
```Include``` n'est pas utilisé ensuite dans ```db.Morceaux``` car les
|
||||
```Album``` ont déjà été chargés depuis la connexion.
|
||||
Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'abord
|
||||
```db.Morceaux.Include(m => m.Album)``` puis simplement ```db.Albums```.
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
* La suite de l'exemple ajoute un nouvel ```Album``` et ses ```Morceau``` puis affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
//...
|
||||
|
||||
WriteLine("\nAjout d'un album et 6 morceaux...\n");
|
||||
|
||||
Album captainMarvel = new Album { Titre = "Captain Marvel", DateDeSortie = new DateTime(1972, 3, 3) };
|
||||
Morceau[] morceaux = { new Morceau { Titre = "La Fiesta", Album = captainMarvel },
|
||||
new Morceau { Titre = "Five Hundred Miles High", Album = captainMarvel },
|
||||
new Morceau { Titre = "Captain Marvel", Album = captainMarvel },
|
||||
new Morceau { Titre = "Time's Lie", Album = captainMarvel },
|
||||
new Morceau { Titre = "Lush Life", Album = captainMarvel },
|
||||
new Morceau { Titre = "Day Waves", Album = captainMarvel }
|
||||
};
|
||||
foreach (var m in morceaux)
|
||||
{
|
||||
captainMarvel.Morceaux.Add(m);
|
||||
}
|
||||
|
||||
db.Add(captainMarvel);
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans
|
||||
l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les
|
||||
migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package* sous Windows (pour cela,
|
||||
dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis
|
||||
*Console du Gestionnaire de package*) ou le *Terminal* sous MacOSX.
|
||||
* 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_015_OneToMany_conventions
|
||||
```
|
||||
:::note Note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra
|
||||
peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration : vous devez préciser la classe fille de ```DbContext``` à utiliser :
|
||||
soit ```AlbumDBEntities```, soit ```StubbedContext```.
|
||||
```
|
||||
dotnet ef migrations add ex_042_015 --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_015_OneToMany_conventions ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à (si vous avez utilisé
|
||||
```StubbedContext```) :
|
||||
```
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
3: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
12: La Fiesta (album : Captain Marvel)
|
||||
13: Five Hundred Miles High (album : Captain Marvel)
|
||||
14: Captain Marvel (album : Captain Marvel)
|
||||
15: Time's Lie (album : Captain Marvel)
|
||||
16: Lush Life (album : Captain Marvel)
|
||||
17: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## 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 ```AlbumDBEntities``` à la place de ```StubbedContext``` :
|
||||
```
|
||||
dotnet ef migrations add ex_042_015 --context AlbumDBEntities
|
||||
dotnet ef database update --context AlbumDBEntities
|
||||
```
|
||||
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les
|
||||
2 albums du stub.
|
||||
Il pourra ressembler à :
|
||||
```
|
||||
Albums :
|
||||
|
||||
Morceaux :
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: La Fiesta (album : Captain Marvel)
|
||||
2: Five Hundred Miles High (album : Captain Marvel)
|
||||
3: Captain Marvel (album : Captain Marvel)
|
||||
4: Time's Lie (album : Captain Marvel)
|
||||
5: Lush Life (album : Captain Marvel)
|
||||
6: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_015_OneToMany_conventions.Albums.db* qui a été généré par l'exécution du
|
||||
programme et qui se trouve près de *ex_042_015_OneToMany_conventions.csproj*.
|
||||

|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 131 KiB |
@ -1,445 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.10.7. One to Many with Fluent API'
|
||||
sidebar_position: 7
|
||||
description: "explique comment préparer un One to Many avec la Fluent API"
|
||||
---
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
||||
# One to Many with Fluent API
|
||||
|
||||
*25/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple montre comment réaliser une relation *One To Many* entre deux entités
|
||||
avec *Entity Framework Core* et la *Fluent API*.
|
||||
Une version équivalente réalisée avec les *annotations de données* et
|
||||
l'établissement d'une clé étrangère de manière explicite est disponible dans
|
||||
l'exemple
|
||||
[**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations).
|
||||
Une version équivalente réalisée avec les *conventions d'écriture* et
|
||||
l'établissement d'une clé étrangère de manière implicite est disponible dans
|
||||
l'exemple
|
||||
[**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions)
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient quatre classes :
|
||||
* ```Album```
|
||||
* ```Morceau```
|
||||
* ```AlbumDBEntities```
|
||||
* ```StubbedContext```
|
||||
|
||||
### Les classes entités : ```Album``` et ```Morceau```
|
||||
|
||||
La relation **One To Many** est mise en évidence de la manière suivante :
|
||||
un ```Album``` possède plusieurs ```Morceau```, et un ```Morceau``` possède
|
||||
un ```Album```.
|
||||
Un ```Album``` contient différentes propriétés (```Titre```, ```DateDeSortie```)
|
||||
dont ```Morceaux``` de type ```ICollection<Morceau>```.
|
||||
Un ```Morceau``` possède une propriété de type ```string``` (```Titre```), et
|
||||
une propriété ```Album``` de type ```Album```.
|
||||
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
Ce qu'il faut noter :
|
||||
* ```Album``` possède une association vers ```Morceau```
|
||||
```csharp title='Album.cs'
|
||||
public ICollection<Morceau> Morceaux { get; set; } = new List<Morceau>();
|
||||
```
|
||||
* ```Morceau``` possède une association vers ```Album```
|
||||
```csharp title='Morceau.cs'
|
||||
public Album Album { get; set; }
|
||||
```
|
||||
* ```Album``` possède un identifiant unique ```AlbumId``` (qui sera la clé
|
||||
* primaire et sera généré par la base lors de l'insertion, cf. ```AlbumDBEntities```).
|
||||
* ```Morceau``` possède un identifiant unique ```MorceauId``` (qui sera la clé
|
||||
* primaire et sera généré par la base lors de l'insertion, cf. ```AlbumDBEntities```).
|
||||
* Le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table
|
||||
de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau```
|
||||
est lié.
|
||||
Dans le cas de l'utilisation de la *Fluent API*, ceci peut se faire dans la classe
|
||||
qui dérive de ```DbContext```. Mais il est possible de le faire de manière explicite
|
||||
en utilisant les *annotations de données* (cf. [**2.10.5. One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations))
|
||||
ou de manière implicite en utilisant les *conventions d'écriture*
|
||||
(cf. [**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions)).
|
||||
Ici, on __N'__ ajoute __PAS__ de nouvelle propriété à ```Morceau``` pour stocker
|
||||
l'identifiant unique de ```Album``` comme dans l'exemple [**2.10.6. One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions)),
|
||||
mais on pourrait le faire (cf. commentaires plus bas).
|
||||
|
||||
### La classe ```AlbumDBEntities```
|
||||
|
||||
* Comme dans les exemples précédents, ```AlbumDBEntities``` dérive de ```DbContext```.
|
||||
* ```AlbumDBEntities``` déclare deux ```DbSet``` : un de ```Album``` et
|
||||
l'autre de ```Morceau```.
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
public DbSet<Album> Albums { get; set; }
|
||||
public DbSet<Morceau> Morceaux { get; set; }
|
||||
```
|
||||
Dans la méthode ```OnModelCreating``` :
|
||||
* on définit la clé primaire de ```Album``` (ceci n'est pas obligatoire, puisqu'on
|
||||
ne fait que refaire ce que font déjà les convetions d'écriture).
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
//création de la table Album
|
||||
modelBuilder.Entity<Album>().HasKey(a => a.AlbumId); //définition de la clé primaire
|
||||
modelBuilder.Entity<Album>().Property(a => a.AlbumId)
|
||||
.ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion
|
||||
```
|
||||
* on définit la clé primaire de ```Morceau``` (ceci n'est pas obligatoire, puisqu'on
|
||||
ne fait que refaire ce que font déjà les convetions d'écriture).
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
//création de la table Morceau
|
||||
modelBuilder.Entity<Morceau>().HasKey(m => m.MorceauId); //définition de la clé primaire
|
||||
modelBuilder.Entity<Morceau>().Property(m => m.MorceauId)
|
||||
.ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion
|
||||
```
|
||||
* on ajoute la clé étrangère dans l'entité ```Morceau``` qui permettra d'établir la
|
||||
relation OnToMany avec l'```Album``` concerné. Comme dit plus haut, le lien
|
||||
entre les deux tables va se faire avec l'ajout d'une colonne dans la table
|
||||
de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau```
|
||||
est lié.
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
// Add the shadow property to the model
|
||||
modelBuilder.Entity<Morceau>()
|
||||
.Property<int>("AlbumForeignKey");
|
||||
```
|
||||
<Mermaid chart={`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Album{
|
||||
+AlbumId: int
|
||||
+Titre: string
|
||||
+DateDeSortie: DateTime
|
||||
}
|
||||
class Morceau{
|
||||
+MorceauId: int
|
||||
+Titre: string
|
||||
==> +AlbumForeignKey: int <==
|
||||
}
|
||||
Album "1" -- "*" Morceau : <pre>+Album</pre><br/><pre> +Morceaux</pre>
|
||||
`}/>
|
||||
|
||||
:::caution AlbumForeignKey
|
||||
Notez l'ajout de la *shadow property* ```AlbumForeignKey``` via la Fluent API
|
||||
|
||||
dans ```Album``` : ```ForeignKey: AlbumForeignKey```
|
||||
:::
|
||||
|
||||
* on décrit ensuite la relation *OneToMany* avec les lignes suivantes, qu'on
|
||||
peut interpréter de la manière suivante :
|
||||
|
||||
> 1 ```Morceau``` est lié à 1 ```Album``` qui lui-même est lié à
|
||||
PLUSIEURS ```Morceau``` en utilisant la clé étrangère ```HasForeignKey```
|
||||
|
||||
```csharp title='AlbumDBEntities.cs'
|
||||
// Use the shadow property as a foreign key
|
||||
modelBuilder.Entity<Morceau>()
|
||||
.HasOne(m => m.Album)
|
||||
.WithMany(a => a.Morceaux)
|
||||
.HasForeignKey("AlbumForeignKey");
|
||||
```
|
||||
* notez enfin que la clé étrangère est ici également une *shadow property*
|
||||
puisqu'elle est définie dans ```OnModelCreating``` et pas directement dans la
|
||||
classe entité (ici ```Morceau```).
|
||||
|
||||
### La classe ```StubbedContext```
|
||||
|
||||
* ```StubbedContext``` est une classe fille de ```AlbumDBEntities```.
|
||||
Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère.
|
||||
Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels.
|
||||
En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la
|
||||
méthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis
|
||||
ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
|
||||
```csharp title='StubbedContext.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
Album kindofblue = new Album { AlbumId=1, Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
|
||||
Album dialogue = new Album { AlbumId=2, Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
|
||||
|
||||
modelBuilder.Entity<Album>().HasData(kindofblue, dialogue);
|
||||
|
||||
modelBuilder.Entity<Morceau>().HasData(new { MorceauId = 1, AlbumForeignKey = 1, Titre = "So What" },
|
||||
new { MorceauId = 2, AlbumForeignKey = 1, Titre = "Freddie Freeloader" },
|
||||
new { MorceauId = 3, AlbumForeignKey = 1, Titre = "Blue in Green" },
|
||||
new { MorceauId = 4, AlbumForeignKey = 1, Titre = "All Blues" },
|
||||
new { MorceauId = 5, AlbumForeignKey = 1, Titre = "Flamenco Sketches" },
|
||||
new { MorceauId = 6, AlbumForeignKey = 2, Titre = "Catta" },
|
||||
new { MorceauId = 7, AlbumForeignKey = 2, Titre = "Idle While" },
|
||||
new { MorceauId = 8, AlbumForeignKey = 2, Titre = "Les Noirs Marchant" },
|
||||
new { MorceauId = 9, AlbumForeignKey = 2, Titre = "Dialogue" },
|
||||
new { MorceauId = 10, AlbumForeignKey = 2, Titre = "Ghetto Lights" },
|
||||
new { MorceauId = 11, AlbumForeignKey = 2, Titre = "Jasper" }
|
||||
);
|
||||
}
|
||||
```
|
||||
* Contrairement aux deux exemples précédents, il n'est pas nécessaire de préciser
|
||||
qu'elle est la clé étrangère pour relier les tables puisque cela a été fait
|
||||
dans la classe ```AlbumDBEntities```
|
||||
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés
|
||||
```Album``` de ```Morceau``` et ```Morceaux``` de ```Album```.
|
||||
Le simple fait d'utiliser la clé étrangère (propriété ```AlbumForeignKey```
|
||||
ajoutée précédemment) dans ```Morceau``` est suffisant.
|
||||
* Notez que ce ne sera pas le cas lors d'une utilisation *classique* de nos classes
|
||||
(ajout, modification...). Nous ne donnerons plus les identifiants directement mais
|
||||
les références des propriétés ```Morceaux``` et ```Album```.
|
||||
|
||||
### La classe ```Program```
|
||||
* On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes
|
||||
données si la migration est faite à partir de ```AlbumDBEntites```) ou le stub
|
||||
(si la migration est faite à partir de ```StubbedContext```).
|
||||
:::note Include
|
||||
Notez l'utilisation d'```Include``` dans
|
||||
```db.Albums.Include(a => a.Morceaux)``` sinon, les ```Morceau``` ne sont pas
|
||||
chargés.
|
||||
```Include``` n'est pas utilisé ensuite dans ```db.Morceaux``` car les
|
||||
```Album``` ont déjà été chargés depuis la connexion.
|
||||
Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'abord
|
||||
```db.Morceaux.Include(m => m.Album)``` puis simplement ```db.Albums```.
|
||||
:::
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
* La suite de l'exemple ajoute un nouvel ```Album``` et ses ```Morceau``` puis affiche le contenu de la base de données.
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
//...
|
||||
|
||||
WriteLine("\nAjout d'un album et 6 morceaux...\n");
|
||||
|
||||
Album captainMarvel = new Album { Titre = "Captain Marvel", DateDeSortie = new DateTime(1972, 3, 3) };
|
||||
Morceau[] morceaux = { new Morceau { Titre = "La Fiesta", Album = captainMarvel },
|
||||
new Morceau { Titre = "Five Hundred Miles High", Album = captainMarvel },
|
||||
new Morceau { Titre = "Captain Marvel", Album = captainMarvel },
|
||||
new Morceau { Titre = "Time's Lie", Album = captainMarvel },
|
||||
new Morceau { Titre = "Lush Life", Album = captainMarvel },
|
||||
new Morceau { Titre = "Day Waves", Album = captainMarvel }
|
||||
};
|
||||
foreach (var m in morceaux)
|
||||
{
|
||||
captainMarvel.Morceaux.Add(m);
|
||||
}
|
||||
|
||||
db.Add(captainMarvel);
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
```csharp title='Program.cs'
|
||||
using (AlbumDBEntities db = new AlbumDBEntities())
|
||||
{
|
||||
WriteLine("Albums : ");
|
||||
foreach (var a in db.Albums.Include(a => a.Morceaux))
|
||||
{
|
||||
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
|
||||
foreach (var m in a.Morceaux)
|
||||
{
|
||||
WriteLine($"\t\t{m.Titre}");
|
||||
}
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("Morceaux :");
|
||||
foreach (var m in db.Morceaux)
|
||||
{
|
||||
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
|
||||
}
|
||||
}
|
||||
```
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans
|
||||
l'exemple [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : pour générer l'exemple, il vous faut d'abord préparer les
|
||||
migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package* sous Windows (pour cela,
|
||||
dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis
|
||||
*Console du Gestionnaire de package*) ou le *Terminal* sous MacOSX.
|
||||
* 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_016_OneToMany_FluentAPI
|
||||
```
|
||||
:::note Note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra
|
||||
peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration : vous devez préciser la classe fille de ```DbContext``` à utiliser :
|
||||
soit ```AlbumDBEntities```, soit ```StubbedContext```.
|
||||
```
|
||||
dotnet ef migrations add ex_042_016 --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_016_OneToMany_FluentAPI ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI).
|
||||
|
||||
* Le résultat de l'exécution va ressembler à (si vous avez utilisé
|
||||
```StubbedContext```) :
|
||||
```
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Kind of Blue (sorti le : 17/08/1959)
|
||||
So What
|
||||
Freddie Freeloader
|
||||
Blue in Green
|
||||
All Blues
|
||||
Flamenco Sketches
|
||||
2: Dialogue (sorti le : 01/09/1965)
|
||||
Catta
|
||||
Idle While
|
||||
Les Noirs Marchant
|
||||
Dialogue
|
||||
Ghetto Lights
|
||||
Jasper
|
||||
3: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: So What (album : Kind of Blue)
|
||||
2: Freddie Freeloader (album : Kind of Blue)
|
||||
3: Blue in Green (album : Kind of Blue)
|
||||
4: All Blues (album : Kind of Blue)
|
||||
5: Flamenco Sketches (album : Kind of Blue)
|
||||
6: Catta (album : Dialogue)
|
||||
7: Idle While (album : Dialogue)
|
||||
8: Les Noirs Marchant (album : Dialogue)
|
||||
9: Dialogue (album : Dialogue)
|
||||
10: Ghetto Lights (album : Dialogue)
|
||||
11: Jasper (album : Dialogue)
|
||||
12: La Fiesta (album : Captain Marvel)
|
||||
13: Five Hundred Miles High (album : Captain Marvel)
|
||||
14: Captain Marvel (album : Captain Marvel)
|
||||
15: Time's Lie (album : Captain Marvel)
|
||||
16: Lush Life (album : Captain Marvel)
|
||||
17: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## 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 ```AlbumDBEntities``` à la place de ```StubbedContext``` :
|
||||
```
|
||||
dotnet ef migrations add ex_042_016 --context AlbumDBEntities
|
||||
dotnet ef database update --context AlbumDBEntities
|
||||
```
|
||||
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les
|
||||
2 albums du stub.
|
||||
Il pourra ressembler à :
|
||||
```
|
||||
Albums :
|
||||
|
||||
Morceaux :
|
||||
|
||||
Ajout d'un album et 6 morceaux...
|
||||
|
||||
Albums :
|
||||
1: Captain Marvel (sorti le : 03/03/1972)
|
||||
La Fiesta
|
||||
Five Hundred Miles High
|
||||
Captain Marvel
|
||||
Time's Lie
|
||||
Lush Life
|
||||
Day Waves
|
||||
|
||||
Morceaux :
|
||||
1: La Fiesta (album : Captain Marvel)
|
||||
2: Five Hundred Miles High (album : Captain Marvel)
|
||||
3: Captain Marvel (album : Captain Marvel)
|
||||
4: Time's Lie (album : Captain Marvel)
|
||||
5: Lush Life (album : Captain Marvel)
|
||||
6: Day Waves (album : Captain Marvel)
|
||||
```
|
||||
|
||||
## Comment vérifier quelles base et tables ont été créées et leur contenu ?
|
||||
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_016_OneToMany_FluentAPI.Albums.db* qui a été généré par l'exécution du
|
||||
programme et qui se trouve près de *ex_042_016_OneToMany_FluentAPI.csproj*.
|
||||

|
||||
* Choisissez ensuite l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus des deux tables
|
||||

|
||||

|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "2.10. Relationships",
|
||||
"position": 10,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Avec ce chapitre, vous aurez plus de détails sur les relations entre entités"
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
---
|
||||
sidebar_label: 'Introduction'
|
||||
sidebar_position: 1
|
||||
description: 'Avec ce chapitre, vous aurez plus de détails sur les relations entre entités'
|
||||
---
|
||||
|
||||
# Relationships
|
||||
|
||||
On parle de propriété de navigation dès qu'on a une propriété qui ne peut pas être une donnée simple (string, nombre, bool...).
|
||||
Par défaut (*ie* conventions d'écriture), dans une relation,
|
||||
c'est toujours la primary key qui est utilisée.
|
||||
On peut utiliser une autre clé en passant par la notation Fluent.
|
||||
|
||||
|
||||
Le plus commun c'est d'avoir des propriétés de navigation
|
||||
définies sur les deux entités et une clé étrangère (foreign key)
|
||||
du côté de l'entité dépendante.
|
||||
* si a on une paire de propriétés de navigation sur les deux entités, l'une sera l'inverse de l'autre
|
||||
* si l'entité dépendante possède une propriété dont le nom vérifie l'un des patrons suivants, alors elle est configurée comme une foreign key:
|
||||
* ```<navigationPropertyName><principalKeyPropertyName>```
|
||||
* ```<navigationPropertyName>Id```
|
||||
* ```<principalEntityName><principalKeyPropertyName>```
|
||||
* ```<principalEntityName>Id```
|
||||
* si on a aucune propriété de clé étrangère, une shadow property est créée avec le nom ```<navigationPropertyName><principalKeyPropertyName>``` ou ```<principalEntityName><principalKeyPropertyName>```.
|
||||
* Single Property Navigation vs. Pair Navigation Property
|
||||
* Cascade Delete
|
||||
* ex01: single navigation property (conventions & data annotations)
|
||||
* ex02: single navigation property (fluent api)
|
||||
* ex03: one to one (conventions & data annotations)
|
||||
* ex04: one to one (fluent api)
|
||||
* ex05: one to many (conventions & data annotations)
|
||||
* ex06: one to many (fluent api)
|
||||
* ex07: many to many (conventions & data annotations)
|
||||
* ex08: many to many (fluent api)
|
||||
* ex09: shadow property
|
||||
* ex10: dictionaries (fluent api)
|
@ -1,300 +0,0 @@
|
||||
---
|
||||
sidebar_label: '2.7. Value Generation'
|
||||
sidebar_position: 7
|
||||
description: "explique comment faire générer des valeurs automatiquement lors de l'insertion ou de la mise à jour"
|
||||
---
|
||||
|
||||
# Value Generation
|
||||
*07/01/2020 ⋅ Marc Chevaldonné*
|
||||
[**▶ Browse Sample ↗**](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p08_BDD_EntityFramework/ex_042_007_ValueGeneration)
|
||||
|
||||
---
|
||||
|
||||
Cet exemple traite de la génération des valeurs de propriétés par la base de données, lors de l'ajout ou de l'insertion.
|
||||
|
||||
:::info Prérequis
|
||||
Je n'explique pas à travers cet exemple les principes de base d'**Entity Framework Core** et en particulier les chaînes de connexion et le lien entre entité et table.
|
||||
Pour plus de renseignements sur :
|
||||
* les chaînes de connexion : [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings)
|
||||
* les liens entre entités et tables : [**2.1. conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions), [**2.2. data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations) et [**2.3. Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI)
|
||||
|
||||
Il est conseillé d'avoir également lu les exemples sur les clés primaires : [**2.4. Keys with conventions**](/docs/Entity-Framework/Model/EF_CF_KeysConvention), [**2.5. Keys with data annotations**](/docs/Entity-Framework/Model/EF_CF_KeysDataAnnotations) et [**2.6. Keys with Fluent API**](/docs/Entity-Framework/Model/EF_CF_Keys_FluentAPI).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## La génération de valeurs
|
||||
**Entity Framework Core** propose trois solutions de génération de valeurs :
|
||||
* **None** : c'est à l'utilisateur de donner une valeur avant l'insertion, sinon, la valeur par défaut du type est utilisée.
|
||||
Par défaut, c'est le mode utilisé pour toutes les propriétés, sauf les clés primaires. C'est donc uniquement dans ce cas qu'on devra chercher à l'utiliser si on veut gérer soi-même les valeurs des clés.
|
||||
* **Generated on add** : on demande à la base de générer une valeur lors de l'insertion en base d'un nouvel élément.
|
||||
Pour savoir si l'élément est nouveau, **EF Core** regarde si l'entité était déjà en base ou non. Si la valeur de la propriété de la clé primaire à générer est la valeur par défaut (par exemple, ```null``` pour ```string```, ```0```pour ```int```, ```Guid.Empty``` pour ```Guid```...), une nouvelle valeur est générée, sinon, rien n'est changé.
|
||||
Ce mode est souvent utilisé pour les clés primaires ou les dates.
|
||||
* **Generated on add or update** : on demande à la base de générer une valeur lors de l'insertion ou de la mise à jour de l'élément en base.
|
||||
Ce mode est souvent utilisé pour les dates représentant des mises à jour.
|
||||
|
||||
Néanmoins, je trouve que l'utilisation sur des propriétés autres que les clés primaires ou avec des types différents de ```int``` ou ```Guid``` n'est pas simple et varie beaucoup en fonction des fournisseurs.
|
||||
De plus, je pense que la génération de valeurs n'est généralement utilisée que pour des clés primaires, des dates (date de création, date de dernière modification) ou des *timestamps* (mais dans ce cas, d'autres mécanismes sont prévus).
|
||||
C'est la raison pour laquelle je pense qu'il est plus simple de se contenter de la réécriture de la méthode ```SaveChanges``` de votre contexte dérivant de ```DbContext```.
|
||||
Cet exemple propose cette méthode à travers l'ajout d'une date d'insertion et d'une date de dernière modification.
|
||||
|
||||
## La classe ```Nounours```
|
||||
La classe ```Nounours``` utilise dans cet exemple les *data annotations*.
|
||||
Notez l'ajout des propriétés ```DateDInsertion``` et ```DernièreModification```, sans annotations.
|
||||
```csharp title='Nounours.cs'
|
||||
[Table("TableNounours")]
|
||||
public class Nounours
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid UniqueId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Required]
|
||||
[MaxLength(256)]
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Column("Naissance", TypeName = "date")]
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DateTime DateDInsertion
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DateTime DernièreModification
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## La classe NounoursDBEntites
|
||||
|
||||
### Gestion des valeurs par défaut pour les dates
|
||||
Dans la méthode ```OnModelCreating``` de ```NounoursDBEntites```, on ajoute deux lignes permettant de donner une valeur par défaut aux deux dates au cas où l'utilisateur de les rentrerait pas :
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasDefaultValue("");
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDInsertion).HasDefaultValue(DateTime.UtcNow);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DernièreModification).HasDefaultValue(DateTime.UtcNow);
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
```
|
||||
:::note Remarque
|
||||
Ceci n'est pas obligatoire pour la génération de valeurs, c'est juste cadeau en passant. Le paragraphe suivant écrase ceci, sauf pour la ```DateDeNaissance```.
|
||||
:::
|
||||
|
||||
### Génération des valeurs par la base
|
||||
Dans cette méthode, je décide de réécrire la méthode virtuelle ```SaveChanges``` de ```NounoursDBEntities```.
|
||||
```csharp title='NounoursDBEntities.cs'
|
||||
public override int SaveChanges()
|
||||
{
|
||||
ChangeTracker.DetectChanges();
|
||||
|
||||
foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Added))
|
||||
{
|
||||
item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow;
|
||||
item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Modified))
|
||||
{
|
||||
item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
|
||||
}
|
||||
return base.SaveChanges();
|
||||
}
|
||||
```
|
||||
* J'utilise dans cette méthode la propriété ```ChangeTracker``` de ```DbContext``` qui permet de garder la trace des modifications qui sont effectuées localement sur les entités avant la sauvegarde (update, delete, create...).
|
||||
L'appel de la méthode ```DetectChanges()``` permet de garder la trace de toutes les modifications réalisées sur les instances.
|
||||
* ```ChangeTracker``` me donne ainsi l'accès aux états des entités, de type ```EntityState```.
|
||||
* La première requête LINQ me donne accès aux entités de type ```Nounours``` ayant été ajoutées sans être encore sauvegardées.
|
||||
```csharp
|
||||
ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Added)
|
||||
```
|
||||
Je récupère ainsi ces entités et modifie la valeur de leurs propriétés ```DateDInsertion``` et ```DernièreModification``` à maintenant (```DateTime.UtcNow```).
|
||||
```csharp
|
||||
item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow;
|
||||
item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
|
||||
```
|
||||
* Je fais ensuite la même chose en récupérant les entités ayant été mises à jour, c'est-à-dire dont la propriété ```State``` vaut ```EntityState.Modified```, et je modifie leur propriété ```DernièreModification```.
|
||||
```csharp
|
||||
foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Modified))
|
||||
{
|
||||
item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
|
||||
}
|
||||
```
|
||||
* Enfin, j'appelle la méthode ```SaveChanges``` de la classe mère.
|
||||
|
||||
En résumé, les valeurs ne sont pas générées sur la base, mais... on s'en fiche non ?
|
||||
|
||||
## La classe ```Program```
|
||||
Cette classe est le point d'entrée du programme :
|
||||
* Elle crée des instances de ```Nounours```
|
||||
Notez que la dernière ne donne aucune valeur pour les propriétés pour tester les valeurs par défaut.
|
||||
```csharp title='Program.cs'
|
||||
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 ();
|
||||
```
|
||||
* Elle démarre une connexion à la base de données
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide.
|
||||
Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base.
|
||||
Notez la création automatique des ID.
|
||||
:::caution note
|
||||
Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution.
|
||||
:::
|
||||
|
||||
```csharp title='Program.cs'
|
||||
//nettoyage de la base de données
|
||||
if (db.NounoursSet.Count() > 0)
|
||||
{
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
db.NounoursSet.Remove(n);
|
||||
}
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
//ajout des nounours dans la base de données
|
||||
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
|
||||
db.SaveChanges();
|
||||
|
||||
WriteLine("database after cleaning and adding 3 Nounours and saving changes :");
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
```
|
||||
* Elle attend ensuite 3 secondes (pour qu'on puisse voir la différence dans les dates entre la mise à jour et l'ajout), puis modifie le nom du premier ```Nounours```
|
||||
```csharp title='Program.cs'
|
||||
WriteLine("Waits 3 seconds...");
|
||||
System.Threading.Thread.Sleep(3000);
|
||||
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
//modification d'un Nounours existant
|
||||
WriteLine("\nModification of the name of Chewbacca to Chewie");
|
||||
chewie = db.NounoursSet.First();
|
||||
chewie.Nom = "Chewie";
|
||||
db.SaveChanges();
|
||||
}
|
||||
```
|
||||
* Enfin, elle affiche les ```Nounours```de la base une nouvelle fois, et les valeurs des propriétés du dernier Nounours pour montrer les valeurs par défaut : ```""``` pour le ```string```, ```0``` pour l'```int```, ```07/01/2020 00:00:00``` pour le ```DateTime```,
|
||||
à cause des lignes
|
||||
```csharp title='Program.cs'
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasDefaultValue("");
|
||||
```
|
||||
ajoutées à ```OnModelCreating```.
|
||||
|
||||
```csharp title='Program.cs'
|
||||
using (NounoursDBEntities db = new NounoursDBEntities())
|
||||
{
|
||||
foreach (var n in db.NounoursSet)
|
||||
{
|
||||
WriteLine($"\t{n}");
|
||||
}
|
||||
|
||||
WriteLine("\nDisplay the last Nounours with default values");
|
||||
Nounours e = db.NounoursSet.ToList().Last();
|
||||
string nameStr = e.Nom != null ? e.Nom : "null";
|
||||
WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}; Insertion date: {e.DateDInsertion}; LastModified: {e.DernièreModification}") ;
|
||||
}
|
||||
```
|
||||
|
||||
## Comment exécuter cet exemple ?
|
||||
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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_007_ValueGeneration
|
||||
```
|
||||
:::note
|
||||
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
* Migration :
|
||||
```
|
||||
dotnet ef migrations add migration_ex_042_007
|
||||
```
|
||||
* Création de la table :
|
||||
```
|
||||
dotnet ef database update
|
||||
```
|
||||
* Génération et exécution
|
||||
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_007_ValueGeneration**.
|
||||
|
||||
* Le résultat de l'exécution ressemblera à :
|
||||
```
|
||||
database after cleaning and adding 3 Nounours and saving changes :
|
||||
49102173-b82a-4b4d-1d02-08d793b46eb3: Chewbacca (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
|
||||
ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
|
||||
ccee4eaf-4b50-48ec-1d04-08d793b46eb3: (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
|
||||
Waits 3 seconds...
|
||||
|
||||
Modification of the name of Chewbacca to Chewie
|
||||
49102173-b82a-4b4d-1d02-08d793b46eb3: Chewie (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:08)
|
||||
ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
|
||||
ccee4eaf-4b50-48ec-1d04-08d793b46eb3: (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
|
||||
|
||||
Display the last Nounours with default values
|
||||
Name: ; BirthDate: 07/01/2020 00:00:00; Hair count: 0; Insertion date: 07/01/2020 20:59:05; LastModified: 07/01/2020 20:59:05
|
||||
```
|
||||
:::note
|
||||
Notez que la date de modification de Chewie est modifée entre les deux affichages.
|
||||
:::
|
||||
|
||||
* **Comment vérifier le contenu des bases de données SQL Server ?**
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_007_ValueGeneration.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|UniqueId |Nom|Naissance|NbPoils|DateDInsertion|DernièreModification
|
||||
|---|---|---|---|---|---
|
||||
|49102173-b82a-4b4d-1d02-08d793b46eb3|Chewbacca|27/05/1977|1234567|07/01/2020 20:59:05|07/01/2020 20:59:08
|
||||
|ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3|Yoda|21/05/1980|3|07/01/2020 20:59:05|07/01/2020 20:59:05
|
||||
|ccee4eaf-4b50-48ec-1d04-08d793b46eb3||07/01/2020|0|07/01/2020 20:59:05|07/01/2020 20:59:05
|
||||
:::note
|
||||
Les identifiants seront bien sûr différents.
|
||||
:::
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "2. Model",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Ce chapitre s'attardera sur le lien entre le modèle et la base de données. En effet, avec EF, l'accès aux données se fait via le modèle, c'est-à-dire l'ensemble de vos classes (qui seront reliées à des tables créées plus ou moins automatiquement) ainsi qu'un contexte (DbContext) qui représentera une session de connexion avec votre (ou vos) base(s) de données. Je présenterai en conséquence tout d'abord comment écrire des classes pour votre modèle, puis comment écrire les différentes relations classiques (aggrégation, one to one, one to many, many to many, mais aussi les dictionnaires), comment gérer les héritages entre classes du modèle dans la base de données, etc."
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
---
|
||||
sidebar_label: '5.3. MariaDB'
|
||||
sidebar_position: 3
|
||||
description: "explique comment utiliser le fournisseur MariaDB"
|
||||
---
|
||||
|
||||
# Utilisation du fournisseur MariaDB
|
||||
*01/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::note Docker
|
||||
Cette méthode utilise MariaDB à travers un conteneur Docker en local.
|
||||
Vous devez donc installer Docker au préalable, en suivant ce lien par exemple : [https://www.docker.com/products/docker-desktop/](https://www.docker.com/products/docker-desktop/)
|
||||
:::
|
||||
|
||||
## Création du conteneur MariaDB en local
|
||||
|
||||
Dans un terminal, récupérez l'image docker de mariadb :
|
||||
```
|
||||
docker pull mariadb
|
||||
```
|
||||
|
||||
Créez un conteneur à partir de cette image (n'hésitez pas à changer le mot de passe...) :
|
||||
```
|
||||
docker run -p 127.0.0.1:3306:3306 --name mdb -e MARIADB_ROOT_PASSWORD=Password123! -e MARIADB_USER=user -e MARIADB_PASSWORD=pwd -e MARIADB_DATABASE=mydb -d mariadb:latest
|
||||
```
|
||||
|
||||
:::note variables d'environnement
|
||||
N'hésitez pas à changer les variables d'environnement comme le password, le user, le nom de la database, le root password...
|
||||
```-e MARIADB_ROOT_PASSWORD=Password123!``` permet de changer le mot de passe root
|
||||
```-e MARIADB_USER=user``` permet de changer le user
|
||||
```-e MARIADB_PASSWORD=pwd``` permet de changer le user
|
||||
```-e MARIADB_DATABASE=mydb``` permet de changer le user
|
||||
:::
|
||||
|
||||
Exécutez la commande ```mariadb``` dans le conteneur :
|
||||
```
|
||||
docker exec -it mdb mariadb --user root -pPassword123!
|
||||
```
|
||||
|
||||
:::note root password
|
||||
Le root password donné juste après le ```-p``` est celui défini dans la commande ```docker run```.
|
||||
:::
|
||||
|
||||
*Utilisez l'une ou l'autre des 2 méthodes suivantes.*
|
||||
|
||||
## Méthode 1 : en modifiant une classe fille de ```DbContext```
|
||||
Il faut d'abord modifier du code, puis lancer les commandes de migration et de création de la base de données.
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
- Pomelo.EntityFrameworkCore.MySql
|
||||
|
||||
### Code
|
||||
Dans votre classe héritant de DbContext, utilisez la méthode d'extension ```UseMySql```.
|
||||
|
||||
```csharp MyDbContext
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
//...
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//...
|
||||
optionsBuilder.UseMySql("server=localhost;port=3306;user=user;password=pwd;database=mydb", new MySqlServerVersion(new Version(10, 11, 1)));
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note variables
|
||||
Vous devez bien sûr utiliser les variables définies lors de la commande ```docker run```, ou les variables par défaut si vous ne les avez pas définies.
|
||||
:::
|
||||
|
||||
Pour le reste de votre projet EntityFrameworkCore, aucune différence avec les méthodes présentées précédemment.
|
||||
|
||||
### Migrations et création de la base de données
|
||||
Vous pouvez maintenant créer les migrations et la base de données à l'aide des commandes classiques
|
||||
(par exemple depuis le projet dans lequel se trouve votre classe héritant de ```DbContext```) :
|
||||
```
|
||||
dotnet ef migrations add myMigrationName
|
||||
dotnet ef database update
|
||||
```
|
||||
|
||||
## Méthode 2 : injection de ```DbContextOptions```
|
||||
Cette méthode permet d'utiliser une classe héritant de ```DbContext``` (```TheDbContext``` par exemple) sans en changer le code.
|
||||
|
||||
:::caution Condition sur ```OnConfiguring```
|
||||
Il faut néanmoins que la classe fille de ```DbContext``` (par exemple ```TheDbContext```) :
|
||||
1. possède un constructeur permettant de passer un ```DbContextOptions``` en paramètre.
|
||||
```csharp TheDbContext
|
||||
public TheDbContext(DbContextOptions<TheDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
```
|
||||
2. n'impose pas un fournisseur et une chaîne de connexion dans la méthode OnConfiguring,
|
||||
par exemple à l'aide d'un ```if(!optionsBuilder.IsConfigured)```
|
||||
```csharp TheDbContext
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.Use...(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités
|
||||
(cela devrait déjà être fait si vous vous injectez un nouveau fournisseur dans un ```DbContext``` existant déjà) :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
|
||||
Puis installez le package Nuget suivant dans le projet qui contiendra la création des options et l'injection dans le contexte
|
||||
(le projet de démarrage par exemple) :
|
||||
- Pomelo.EntityFrameworkCore.MySql
|
||||
|
||||
### Code
|
||||
On peut dès lors, avant la création du contexte, créer des options permettant de choisir le fournisseur de la manière suivante :
|
||||
```csharp Program.cs (ou autre)
|
||||
var options = new DbContextOptionsBuilder<ArtistsDbContext>()
|
||||
.UseMySql("server=localhost;port=3306;user=user;password=pwd;database=mydb", new MySqlServerVersion(new Version(10, 11, 1)))
|
||||
.Options;
|
||||
var context = new TheDbContext(options); //ou une autre classe dérivant de TheDbContext
|
||||
await context.Database.EnsureCreatedAsync(); //pour créer la base si elle n'existe pas déjà
|
||||
```
|
||||
|
||||
* La première ligne permet de choisir les options, dont le fournisseur (```UseMySql```) et la chaîne de connexion (```"server=localhost;port=3306;user=user;password=pwd;database=mydb"```).
|
||||
* La deuxième ligne crée le contexte en injectant ces options.
|
||||
* La troisième ligne s'assure que la base de données est bien créée ou qu'elle existait déjà.
|
||||
|
||||
:::note variables
|
||||
Vous devez bien sûr utiliser les variables définies lors de la commande ```docker run```, ou les variables par défaut si vous ne les avez pas définies.
|
||||
:::
|
||||
|
||||
:::note migrations ?
|
||||
Avec cette méthode, il n'est pas nécessaire de créer les migrations ou la base de données en lignes de commande.
|
||||
:::
|
@ -1,130 +0,0 @@
|
||||
---
|
||||
sidebar_label: '5.2. Postgres'
|
||||
sidebar_position: 2
|
||||
description: "explique comment utiliser le fournisseur Postgres"
|
||||
---
|
||||
|
||||
# Utilisation du fournisseur Postgres
|
||||
*01/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::note Docker
|
||||
Cette méthode utilise Postgres à travers un conteneur Docker en local.
|
||||
Vous devez donc installer Docker au préalable, en suivant ce lien par exemple : [https://www.docker.com/products/docker-desktop/](https://www.docker.com/products/docker-desktop/)
|
||||
:::
|
||||
|
||||
## Création du conteneur Postgres en local
|
||||
|
||||
Dans un terminal, récupérez l'image docker de postgres :
|
||||
```
|
||||
docker pull postgres
|
||||
```
|
||||
|
||||
Créez un conteneur à partir de cette image (n'hésitez pas à changer le mot de passe...) :
|
||||
```
|
||||
docker run --rm -P -p 127.0.0.1:5432:5432 -e POSTGRES_PASSWORD="1234" --name pg postgres:latest
|
||||
```
|
||||
|
||||
:::note variables d'environnement
|
||||
N'hésitez pas à changer les variables d'environnement comme le password, le user id, le nom de la database...
|
||||
:::
|
||||
|
||||
*Utilisez l'une ou l'autre des 2 méthodes suivantes.*
|
||||
|
||||
## Méthode 1 : en modifiant une classe fille de ```DbContext```
|
||||
Il faut d'abord modifier du code, puis lancer les commandes de migration et de création de la base de données.
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
- Npgsql.EntityFrameworkCore.PostgreSQL
|
||||
|
||||
### Code
|
||||
Dans votre classe héritant de ```DbContext```, utilisez la méthode d'extension ```UseNpgsql```.
|
||||
|
||||
```csharp MyDbContext
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
//...
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//...
|
||||
optionsBuilder.UseNpgsql(@"host=localhost;database=postgres;user id=postgres;password=1234;");
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note variables
|
||||
Vous devez bien sûr utiliser les variables définies lors de la commande ```docker run```, ou les variables par défaut si vous ne les avez pas définies.
|
||||
:::
|
||||
|
||||
Pour le reste de votre projet EntityFrameworkCore, aucune différence avec les méthodes présentées précédemment.
|
||||
|
||||
### Migrations et création de la base de données
|
||||
Vous pouvez maintenant créer les migrations et la base de données à l'aide des commandes classiques
|
||||
(par exemple depuis le projet dans lequel se trouve votre classe héritant de ```DbContext```) :
|
||||
```
|
||||
dotnet ef migrations add myMigrationName
|
||||
dotnet ef database update
|
||||
```
|
||||
|
||||
## Méthode 2 : injection de ```DbContextOptions```
|
||||
Cette méthode permet d'utiliser une classe héritant de ```DbContext``` (```TheDbContext``` par exemple) sans en changer le code.
|
||||
|
||||
:::caution Condition sur ```OnConfiguring```
|
||||
Il faut néanmoins que la classe fille de ```DbContext``` (par exemple ```TheDbContext```) :
|
||||
1. possède un constructeur permettant de passer un ```DbContextOptions``` en paramètre.
|
||||
```csharp TheDbContext
|
||||
public TheDbContext(DbContextOptions<TheDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
```
|
||||
2. n'impose pas un fournisseur et une chaîne de connexion dans la méthode OnConfiguring,
|
||||
par exemple à l'aide d'un ```if(!optionsBuilder.IsConfigured)```
|
||||
```csharp TheDbContext
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.Use...(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités
|
||||
(cela devrait déjà être fait si vous vous injectez un nouveau fournisseur dans un ```DbContext``` existant déjà) :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
|
||||
Puis installez le package Nuget suivant dans le projet qui contiendra la création des options et l'injection dans le contexte
|
||||
(le projet de démarrage par exemple) :
|
||||
- Npgsql.EntityFrameworkCore.PostgreSQL
|
||||
|
||||
### Code
|
||||
On peut dès lors, avant la création du contexte, créer des options permettant de choisir le fournisseur de la manière suivante :
|
||||
```csharp Program.cs (ou autre)
|
||||
var options = new DbContextOptionsBuilder<ArtistsDbContext>()
|
||||
.UseNpgsql(@"host=localhost;database=postgres;user id=postgres;password=1234;")
|
||||
.Options;
|
||||
var context = new TheDbContext(options); //ou une autre classe dérivant de TheDbContext
|
||||
await context.Database.EnsureCreatedAsync(); //pour créer la base si elle n'existe pas déjà
|
||||
```
|
||||
|
||||
* La première ligne permet de choisir les options, dont le fournisseur (```UseNpgsql```) et la chaîne de connexion (```"host=localhost;database=postgres;user id=postgres;password=1234;"```).
|
||||
* La deuxième ligne crée le contexte en injectant ces options.
|
||||
* La troisième ligne s'assure que la base de données est bien créée ou qu'elle existait déjà.
|
||||
|
||||
:::note variables
|
||||
Vous devez bien sûr utiliser les variables définies lors de la commande ```docker run```, ou les variables par défaut si vous ne les avez pas définies.
|
||||
:::
|
||||
|
||||
:::note migrations ?
|
||||
Avec cette méthode, il n'est pas nécessaire de créer les migrations ou la base de données en lignes de commande.
|
||||
:::
|
@ -1,133 +0,0 @@
|
||||
---
|
||||
sidebar_label: '5.1. SQLite'
|
||||
sidebar_position: 1
|
||||
description: "explique comment utiliser le fournisseur SQLite"
|
||||
---
|
||||
|
||||
# Utilisation du fournisseur SQLite
|
||||
*02/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::note base de données en local
|
||||
Cette méthode utilise SQLite à travers une base de données dans un fichier local.
|
||||
Pour explorer le contenu de la base, vous pouvez utiliser un logiciel comme [**DB Browser for SQLite**](https://sqlitebrowser.org/dl/)
|
||||
:::
|
||||
|
||||
*Utilisez l'une ou l'autre des 2 méthodes suivantes.*
|
||||
|
||||
## Méthode 1 : en modifiant une classe fille de ```DbContext```
|
||||
Il faut d'abord modifier du code, puis lancer les commandes de migration et de création de la base de données.
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
- Microsoft.EntityFrameworkCore.SQLite
|
||||
|
||||
### Code
|
||||
Dans votre classe héritant de ```DbContext```, utilisez la méthode d'extension ```UseSqlite```.
|
||||
|
||||
```csharp MyDbContext
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
//...
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//...
|
||||
optionsBuilder.UseSqlite($"Data Source=LeNomDe.MaBase.db");
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dans le projet de démarrage (.csproj) qui contiendra la base de données et devra l'utiliser,
|
||||
n'oubliez pas de rajouter la ligne suivante pour lui permettre de la trouver dans le bon dossier :
|
||||
```xml StartupProject.csproj
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
...
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
...
|
||||
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
...
|
||||
</Project>
|
||||
```
|
||||
|
||||
Pour le reste de votre projet EntityFrameworkCore, aucune différence avec les méthodes présentées précédemment.
|
||||
|
||||
### Migrations et création de la base de données
|
||||
Vous pouvez maintenant créer les migrations et la base de données à l'aide des commandes classiques
|
||||
(par exemple depuis le projet dans lequel se trouve votre classe héritant de ```DbContext```) :
|
||||
```
|
||||
dotnet ef migrations add myMigrationName
|
||||
dotnet ef database update --startup-project path/to/the/startup/project/
|
||||
```
|
||||
|
||||
## Méthode 2 : injection de ```DbContextOptions```
|
||||
Cette méthode permet d'utiliser une classe héritant de ```DbContext``` (```TheDbContext``` par exemple) sans en changer le code.
|
||||
|
||||
:::caution Condition sur ```OnConfiguring```
|
||||
Il faut néanmoins que la classe fille de ```DbContext``` (par exemple ```TheDbContext```) :
|
||||
1. possède un constructeur permettant de passer un ```DbContextOptions``` en paramètre.
|
||||
```csharp TheDbContext
|
||||
public TheDbContext(DbContextOptions<TheDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
```
|
||||
2. n'impose pas un fournisseur et une chaîne de connexion dans la méthode OnConfiguring,
|
||||
par exemple à l'aide d'un ```if(!optionsBuilder.IsConfigured)```
|
||||
```csharp TheDbContext
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.Use...(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités
|
||||
(cela devrait déjà être fait si vous vous injectez un nouveau fournisseur dans un ```DbContext``` existant déjà) :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
|
||||
Puis installez le package Nuget suivant dans le projet qui contiendra la création des options et l'injection dans le contexte
|
||||
(le projet de démarrage par exemple) :
|
||||
- Npgsql.EntityFrameworkCore.Sqlite
|
||||
|
||||
### Code
|
||||
On peut dès lors, avant la création du contexte, créer des options permettant de choisir le fournisseur de la manière suivante :
|
||||
```csharp Program.cs (ou autre)
|
||||
var options = new DbContextOptionsBuilder<TheDbContext>().UseSqlite($"Data Source=LeNomDe.MaBase.db").Options;
|
||||
var context = new TheDbContext(options); //ou une autre classe dérivant de TheDbContext
|
||||
await context.Database.EnsureCreatedAsync(); //pour créer la base si elle n'existe pas déjà
|
||||
```
|
||||
|
||||
* La première ligne permet de choisir les options, dont le fournisseur (```UseSqlite```) et la chaîne de connexion (```"Data Source=LeNomDe.MaBase.db"```).
|
||||
* La deuxième ligne crée le contexte en injectant ces options.
|
||||
* La troisième ligne s'assure que la base de données est bien créée ou qu'elle existait déjà.
|
||||
|
||||
:::note migrations ?
|
||||
Avec cette méthode, il n'est pas nécessaire de créer les migrations ou la base de données en lignes de commande.
|
||||
:::
|
||||
|
||||
## Comment explorer le contenu de la base de données ?
|
||||
|
||||
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 *XXXX.XXXX.db* qui a été généré par l'exécution du programme.
|
||||

|
||||
* Choisissez l'onglet *Parcourir les données*
|
||||
* Observez les résultats obtenus
|
||||

|
||||
* Vous devriez maintenant pouvoir voir les données.
|
@ -1,113 +0,0 @@
|
||||
---
|
||||
sidebar_label: '5.5. SQLite In Memory'
|
||||
sidebar_position: 5
|
||||
description: "explique comment utiliser le fournisseur SQLite In Memory"
|
||||
---
|
||||
|
||||
# Utilisation du fournisseur SQLite In Memory
|
||||
*02/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::note base de données en mémoire
|
||||
Cette méthode utilise SQLite à travers une base de données directement en mémoire, et donc, non stockée.
|
||||
Sa durée de vie est donc limitée à l'exécution du programme.
|
||||
Cette méthode est donc préconisée pour des tests unitaires ou fonctionnels.
|
||||
:::
|
||||
|
||||
*Utilisez l'une ou l'autre des 2 méthodes suivantes.*
|
||||
|
||||
## Méthode 1 : en modifiant une classe fille de ```DbContext```
|
||||
Il faut d'abord modifier du code, puis lancer les commandes de migration et de création de la base de données.
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
- Microsoft.EntityFrameworkCore.SQLite
|
||||
|
||||
### Code
|
||||
Dans votre classe héritant de ```DbContext```, utilisez la méthode d'extension ```UseSqlite```.
|
||||
|
||||
```csharp MyDbContext
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
//...
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//...
|
||||
var connection = new SqliteConnection("DataSource=:memory:");
|
||||
connection.Open();
|
||||
optionsBuilder.UseSqlite(connection);
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
De plus, au moment de l'instanciation de votre contexte, n'oubliez pas de vérifier ou de créer la base de données procéduralement de la manière suivante :
|
||||
```csharp Program.cs (ou tout autre endroit ou le contexte est initialisé)
|
||||
var context = new MyDbContext();
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
```
|
||||
|
||||
Pour le reste de votre projet EntityFrameworkCore, aucune différence avec les méthodes présentées précédemment.
|
||||
|
||||
### Migrations et création de la base de données
|
||||
Il n'est pas nécessaire de créer des migrations ou la table en utilisant les lignes de commande.
|
||||
|
||||
## Méthode 2 : injection de ```DbContextOptions```
|
||||
Cette méthode permet d'utiliser une classe héritant de ```DbContext``` (```TheDbContext``` par exemple) sans en changer le code.
|
||||
|
||||
:::caution Condition sur ```OnConfiguring```
|
||||
Il faut néanmoins que la classe fille de ```DbContext``` (par exemple ```TheDbContext```) :
|
||||
1. possède un constructeur permettant de passer un ```DbContextOptions``` en paramètre.
|
||||
```csharp TheDbContext
|
||||
public TheDbContext(DbContextOptions<TheDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
```
|
||||
2. n'impose pas un fournisseur et une chaîne de connexion dans la méthode OnConfiguring,
|
||||
par exemple à l'aide d'un ```if(!optionsBuilder.IsConfigured)```
|
||||
```csharp TheDbContext
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.Use...(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités
|
||||
(cela devrait déjà être fait si vous vous injectez un nouveau fournisseur dans un ```DbContext``` existant déjà) :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
|
||||
Puis installez le package Nuget suivant dans le projet qui contiendra la création des options et l'injection dans le contexte
|
||||
(le projet de démarrage par exemple) :
|
||||
- Npgsql.EntityFrameworkCore.Sqlite
|
||||
|
||||
### Code
|
||||
On peut dès lors, avant la création du contexte, créer des options permettant de choisir le fournisseur de la manière suivante :
|
||||
```csharp Program.cs (ou autre)
|
||||
var connection = new SqliteConnection("DataSource=:memory:");
|
||||
connection.Open();
|
||||
var options = new DbContextOptionsBuilder<TheDbContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
var context = new TheDbContext(options);
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
```
|
||||
|
||||
* Les deux premières lignes permettent d'ouvrir une connexion de type Sqlite vers une base de données en mémoire en spécifiant la chaîne de connexion (```"DataSource=:memory:"```), spéciale pour SqliteInMemory.
|
||||
* La troisième crée les options en spécifiant le fournisseur ```UseSqlite``` et la connexion (donc le fait qu'on utilise SqliteInMemory).
|
||||
* La quatrième ligne crée le contexte en injectant ces options.
|
||||
* La cinquième ligne s'assure que la base de données est bien créée ou qu'elle existait déjà.
|
||||
|
||||
:::note migrations ?
|
||||
Avec cette méthode, il n'est pas nécessaire de créer les migrations ou la base de données en lignes de commande.
|
||||
:::
|
@ -1,128 +0,0 @@
|
||||
---
|
||||
sidebar_label: '5.4. SqlServer'
|
||||
sidebar_position: 4
|
||||
description: "explique comment utiliser le fournisseur SqlServer (seulement windows)"
|
||||
---
|
||||
|
||||
# Utilisation du fournisseur SqlServer (seulement Windows)
|
||||
*02/02/2023 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
:::note base de données en local
|
||||
Cette méthode utilise SqlServer à travers une base de données dans en local.
|
||||
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server* (voir plus bas).
|
||||
:::
|
||||
|
||||
*Utilisez l'une ou l'autre des 2 méthodes suivantes.*
|
||||
|
||||
## Méthode 1 : en modifiant une classe fille de ```DbContext```
|
||||
Il faut d'abord modifier du code, puis lancer les commandes de migration et de création de la base de données.
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (windows seulement), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
- Microsoft.EntityFrameworkCore.SqlServer
|
||||
|
||||
### Code
|
||||
Dans votre classe héritant de ```DbContext```, utilisez la méthode d'extension ```UseSqlServer```.
|
||||
|
||||
```csharp MyDbContext
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
//...
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//...
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=LeNomDe.MaBase.mdf;Trusted_Connection=True;");
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pour le reste de votre projet EntityFrameworkCore, aucune différence avec les méthodes présentées précédemment.
|
||||
|
||||
### Migrations et création de la base de données
|
||||
Vous pouvez maintenant créer les migrations et la base de données à l'aide des commandes classiques
|
||||
(par exemple depuis le projet dans lequel se trouve votre classe héritant de ```DbContext```) :
|
||||
```
|
||||
dotnet ef migrations add myMigrationName
|
||||
dotnet ef database update --startup-project path/to/the/startup/project/
|
||||
```
|
||||
|
||||
## Méthode 2 : injection de ```DbContextOptions```
|
||||
Cette méthode permet d'utiliser une classe héritant de ```DbContext``` (```TheDbContext``` par exemple) sans en changer le code.
|
||||
|
||||
:::caution Condition sur ```OnConfiguring```
|
||||
Il faut néanmoins que la classe fille de ```DbContext``` (par exemple ```TheDbContext```) :
|
||||
1. possède un constructeur permettant de passer un ```DbContextOptions``` en paramètre.
|
||||
```csharp TheDbContext
|
||||
public TheDbContext(DbContextOptions<TheDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
```
|
||||
2. n'impose pas un fournisseur et une chaîne de connexion dans la méthode OnConfiguring,
|
||||
par exemple à l'aide d'un ```if(!optionsBuilder.IsConfigured)```
|
||||
```csharp TheDbContext
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if(!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.Use...(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### NuGet
|
||||
Depuis Visual Studio (mac ou windows), installez les packages NuGet suivants, dans le projet contenant votre ```DbContext``` et vos entités
|
||||
(cela devrait déjà être fait si vous vous injectez un nouveau fournisseur dans un ```DbContext``` existant déjà) :
|
||||
- Microsoft.EntityFrameworkCore
|
||||
- Microsoft.EntityFrameworkCore.Tools
|
||||
|
||||
Puis installez le package Nuget suivant dans le projet qui contiendra la création des options et l'injection dans le contexte
|
||||
(le projet de démarrage par exemple) :
|
||||
- Microsoft.EntityFrameworkCore.SqlServer
|
||||
|
||||
### Code
|
||||
On peut dès lors, avant la création du contexte, créer des options permettant de choisir le fournisseur de la manière suivante :
|
||||
```csharp Program.cs (ou autre)
|
||||
var options = new DbContextOptionsBuilder<TheDbContext>()
|
||||
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=LeNomDe.MaBase.mdf;Trusted_Connection=True;")
|
||||
.Options;
|
||||
var context = new TheDbContext(options); //ou une autre classe dérivant de TheDbContext
|
||||
await context.Database.EnsureCreatedAsync(); //pour créer la base si elle n'existe pas déjà
|
||||
```
|
||||
|
||||
* La première ligne permet de choisir les options, dont le fournisseur (```UseSqlServer```) et la chaîne de connexion (```@"Server=(localdb)\mssqllocaldb;Database=LeNomDe.MaBase.mdf;Trusted_Connection=True;"```).
|
||||
* La deuxième ligne crée le contexte en injectant ces options.
|
||||
* La troisième ligne s'assure que la base de données est bien créée ou qu'elle existait déjà.
|
||||
|
||||
:::note migrations ?
|
||||
Avec cette méthode, il n'est pas nécessaire de créer les migrations ou la base de données en lignes de commande.
|
||||
:::
|
||||
|
||||
|
||||
## Comment explorer le contenu de la base de données ?
|
||||
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server* (voir plus bas).
|
||||
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||

|
||||
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, ici par exemple : *myFirstDatabase.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table (par exemple ici : *dbo.Nounours*) puis choisissez *Afficher les données*
|
||||

|
||||
|
||||
* Vous devriez maintenant pouvoir voir les données dans le tableau.
|
||||
|
||||
Notez qu'il est également possible d'utiliser l'*Explorateur d'objets SQL Server* pour ajouter, modifier ou supprimer des données dans les tables.
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "5. Providers",
|
||||
"position": 5,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Comment utiliser différents fournisseurs ?"
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "Entity Framework Core",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "5 minutes to learn the most important Docusaurus concepts."
|
||||
}
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
---
|
||||
sidebar_label: 'Introduction'
|
||||
sidebar_position: 1
|
||||
description: 'Start here'
|
||||
---
|
||||
|
||||
# Entity Framework Core 3.0
|
||||
*25/01/2020 ⋅ Marc Chevaldonné*
|
||||
*Dernière modification : 04/10/2022 ⋅ Marc Chevaldonné*
|
||||
|
||||
|
||||
Entity Framework (EF) Core est un ORM (Object-Relational Mapper) qui permet aux développeurs .NET de gérer de manière simple, légère et extensible, des bases de données.
|
||||
EF permet de gérer de nombreux *providers* (SQL Server, SQLite, Cosmos, ...) de manière transparente.
|
||||
EF vous permet également de mettre à jour vos bases de données et d'exécuter des requêtes sans avoir à écrire la moindre requête SQL. Vous pouvez passer par LINQ to SQL qui apportera plus de lisibilité et permettra au compilateur de vous aider à détecter vos erreurs.
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
La suite de cette section est composée d'exemples de code commentés. Pour mieux les appréhender, voici tout d'abord un ensemble de vidéos d'introduction. Leur but n'est pas d'être exhaustives, mais seulement de vous permettre de visualiser ce qu'il est possible de faire avec Entity Framework Core, et ainsi de parcourir plus efficacement les tutoriels et exemples détaillés.
|
||||
|
||||
### Introduction : une base, une table, quelques requêtes...
|
||||
Cette première vidéo sert d'introduction. Elle présente Entity Framework Core, comment créer une base de données, lui ajouter une table, la remplir et exécuter des requêtes.
|
||||
<iframe allowfullscreen src='https://opencast.dsi.uca.fr/paella/ui/embed.html?id=a5261c69-ecba-4cf7-916a-50113893534e' width='432' height='270' frameborder='0' scrolling='no' marginwidth='0' marginheight='0' allowfullscreen='true' webkitallowfullscreen='true' mozallowfullscreen='true'/>
|
||||
<br/><br/>
|
||||
|
||||
Dans cette seconde vidéo, vous verrez comment construire votre base de données et votre table tout en utilisant une bibliothèque de classes. Il y a aussi une introduction aux trois approches proposées par Entity Framework Core : conventions de nommage, annotations de données et Fluent API.
|
||||
<br/><br/><iframe allowfullscreen src='https://opencast.dsi.uca.fr/paella/ui/embed.html?id=cd47b6f1-9673-47d3-820e-2ac3324f4510' width='432' height='270' frameborder='0' scrolling='no' marginwidth='0' marginheight='0' allowfullscreen='true' webkitallowfullscreen='true' mozallowfullscreen='true'/><br/><br/>
|
||||
|
||||
Cette troisième vidéo traite d'architecture. Elle montre comment organiser son code entre entités, modèle, en utilisant l'injection de dépendance. Elle montre également comment utiliser les méthodes asynchrones pour l'utilisation de la base de données, et comment utiliser les méthodes d'extenions pour rendre le code plus lisible.
|
||||
<iframe allowfullscreen src='https://opencast.dsi.uca.fr/paella/ui/embed.html?id=c7091511-db70-46ce-b599-2190a8fe8b8d' width='432' height='270' frameborder='0' scrolling='no' marginwidth='0' marginheight='0' allowfullscreen='true' webkitallowfullscreen='true' mozallowfullscreen='true'/><br/><br/>
|
||||
|
||||
Enfin, pour finir, voici la vidéo du cours du 24 janvier 2023 pour ceux qui n'auraient pas eu le temps de prendre des notes correctement...
|
||||
<iframe allowfullscreen src='https://opencast.dsi.uca.fr/paella/ui/watch.html?id=196c45ea-eedc-4514-8a81-3b883fb2da47' width='432' height='270' frameborder='0' scrolling='no' marginwidth='0' marginheight='0' allowfullscreen='true' webkitallowfullscreen='true' mozallowfullscreen='true'/><br/><br/>
|
||||
|
||||
:::note
|
||||
Différentes solutions existent avec EF pour gérer une base de données dont le modèle existe par exemple. Dans ces exemples, je ne traiterai que la partie *Code First*, c'est-à-dire le cas où le modèle est créé à partir de vos classes.
|
||||
:::
|
||||
|
||||
## Plan
|
||||
Les exemples sont organisés selon le plan suivant:
|
||||
1. [**Fundamentals** :](/docs/category/1. Fundamentals)
|
||||
Dans cette partie, je donnerai quelques notions pour se connecter à une base à l'aide de chaîne de connection (*connection strings*), comment utiliser des *providers de tests...*.
|
||||
Il s'agira en conséquence d'exemples simples manquants d'explications sur certains points, car ils seront présentés plus tard.
|
||||
* [**1.1. Connection Strings**](/docs/Entity-Framework/Fundamentals/ConnectionStrings) : montre comment utiliser une chaîne de connexion SQL Server ou SQLite.
|
||||
* [**1.2. Testing in memory**](/docs/Entity-Framework/Fundamentals/TestingInMemory) : présente comment utiliser des fournisseurs en mémoire pour éviter la surchage de la création d'une base de données en particulier dans le cas de tests unitaires. Cet exemple est composé de 4 projets.
|
||||
2. *Model* :
|
||||
Ce chapitre s'attardera sur le lien entre le modèle et la base de données. En effet, avec EF, l'accès aux données se fait via le modèle, c'est-à-dire l'ensemble de vos classes (qui seront reliées à des tables créées plus ou moins automatiquement)
|
||||
ainsi qu'un contexte (```DbContext```) qui représentera une session de connexion avec votre (ou vos) base(s) de données.
|
||||
Je présenterai en conséquence tout d'abord comment écrire des classes pour votre modèle, puis comment écrire les différentes relations classiques (aggrégation, *one to one*, *one to many*, *many to many*, mais aussi les dictionnaires), comment gérer les héritages entre classes du modèle dans la base de données, etc.
|
||||
* [**2.1. : conventions d'écriture**](/docs/Entity-Framework/Model/EF_CF_namingConventions) : explique quelles sont les conventions d'écriture utilisées pour la transformation d'une entité en table.
|
||||
* [**2.2. : data annotations**](/docs/Entity-Framework/Model/EF_CF_dataAnnotations) : explique comment utiliser les *data annotations* pour personnaliser la transformation d'une entité en table.
|
||||
* [**2.3. : Fluent API**](/docs/Entity-Framework/Model/EF_CF_FluentAPI) : explique comment utiliser la *Fluent API* pour personnaliser la transformation d'une entité en table.
|
||||
* [**2.4. : Keys with conventions**](/docs/Entity-Framework/Model/EF_CF_KeysConvention) : explique comment créer les clés primaires d'une entité lorsqu'on utilise les conventions d'écriture.
|
||||
* [**2.5. : Keys with data annotations**](/docs/Entity-Framework/Model/EF_CF_KeysDataAnnotations) : explique comment créer les clés primaires d'une entité lorsqu'on utilise les *data annotations*.
|
||||
* [**2.6. : Keys with Fluent API**](/docs/Entity-Framework/Model/EF_CF_Keys_FluentAPI) : explique comment créer les clés primaires d'une entité lorsqu'on utilise la *Fluent API*.
|
||||
* [**2.7. : Value Generation**](/docs/Entity-Framework/Model/ValueGeneration) : explique comment faire générer des valeurs automatiquement lors de l'insertion ou de la mise à jour
|
||||
* [**2.8 : Data Seeding before Entity Framework 2.1**](/docs/Entity-Framework/Model/DataSeedingBeforeEF2_1) : explique comment utiliser un stub (méthode qui était recommandée avant EF Core 2.1)
|
||||
* [**2.9 : Data Seeding**](/docs/Entity-Framework/Model/DataSeeding) : explique comment utiliser un stub (méthode recommandée depuis EF Core 2.1)
|
||||
* [**2.10 : Relationships**](/docs/Entity-Framework/Model/Relationships/intro.md): où vous aurez plus de détails sur les relations entre entités
|
||||
* [**2.10.1. Single Property Navigation with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_01_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.
|
||||
* [**2.10.2. : Single Property navigation with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_02_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*.
|
||||
* [**2.10.3. : One To One with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_03_OneToOne_dataAnnotations) : 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*.
|
||||
* [**2.10.4. : One To One with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_04_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*.
|
||||
* [**2.10.5. : One To Many with data annotations**](/docs/Entity-Framework/Model/Relationships/02_10_05_OneToMany_dataAnnotations) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant l'*annotation de données*.
|
||||
* [**2.10.6. : One To Many with naming conventions**](/docs/Entity-Framework/Model/Relationships/02_10_06_OneToMany_conventions) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant les *conventions d'écriture*.
|
||||
* [**2.10.7. : One To Many with Fluent API**](/docs/Entity-Framework/Model/Relationships/02_10_07_OneToMany_FluentAPI) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant la *Fluent API*.
|
||||
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* :
|
||||
*Language INtegrated Query* (LINQ) est un outil de requête sur collections et sa version LINQ to SQL vous permet de passer très facilement à un système de requêtes sur les bases de données.
|
||||
Les requêtes LINQ sont automatiquement traduites en requêtes SQL, vous évitant ainsi d'avoir à écrire vos requêtes vous-mêmes. Elles sont dès lors beaucoup plus lisibles et faciles à écrire.
|
||||
Ce chapitre présente comment charger des données, réaliser du filtrage, de manière synchrone ou asynchrone, etc.
|
||||
Il montre bien sûr également comment réaliser le symétrique : mettre à jour, supprimer ou ajouter de nouvelles données dans la base.
|
||||
5. *Database providers* :
|
||||
EF vous permet de gérer votre base de données indépendamment du *provider*. Ce chapitre montrera donc comment utiliser différents providers parmi lesquels Microsoft SQL Server, SQLite ou encore InMemory dont le but est de permettre de tester la base en mémoire, sans passer par un *provider*.
|
||||
|
||||
---
|
||||
|
||||
## Quelle version utiliser ?
|
||||
Ces exemples sont écrits pour .NET Core 3.0, mais vous pouvez utiliser EF Core avec différents types projets. Voici les recommendations actuelles de Microsoft quant à l'utilisation des version d'EF.
|
||||
|
||||
|**EF Core** |**1.x** |**2.x** |**3.x**
|
||||
|----------------|--------|-----------|---------------
|
||||
|.NET Standard |1.3 |2.0 |2.1
|
||||
|.NET Core |1.0 |2.0 |3.0
|
||||
|.NET Framework |4.5.1 |4.7.2 |(not supported)
|
||||
|Mono |4.6 |5.4 |6.4
|
||||
|Xamarin.iOS |10.0 |10.14 |12.16
|
||||
|Xamarin.Android |7.0 |8.0 |10.0
|
||||
|UWP |10.0 |10.0.16299 |to be defined
|
||||
|Unity |2018.1 |2018.1 |to be defined
|
||||
|
||||
:::tip Comment lire ce tableau ?
|
||||
|
||||
Si vous voulez utiliser EF Core 3.0 avec une bibliothèque de classes écrites en .NET Standard, celle-ci doit utiliser au moins .NET Standard 2.1.
|
||||
|
||||
Si vous voulez utiliser EF Core 3.0 avec un projet Xamarin.iOS, celui-ci doit être au moins en version 12.16.
|
||||
|
||||
Si vous voulez utiliser EF Core dans une application UWP, vous ne pouvez pour le moment utiliser que EF Core 1.x ou 2.x.
|
||||
|
||||
:::
|
||||
|
||||
:::note
|
||||
|
||||
Je n'ai pas l'intention de mettre à jour les exemples pour Entity Framework 6 ou pour .NET Framework, puisque la version 5 du framework va unifier .NET Framework et .NET Core. En conséquence, EF Core sera la nouvelle "norme".
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
## Comment commencer ?
|
||||
Un petit tutoriel rapide pour savoir comment créer un projet...
|
||||
##### Prérequis
|
||||
Il vous faut au moins la **[version 3.0 du SDK de .NET Core](https://dotnet.microsoft.com/download)**, mais celle-ci est certainement déjà installée si vous avez installé Visual Studio 2019 16.3 ou plus.
|
||||
##### Créez un nouveau projet
|
||||
Vous pouvez ensuite créer un nouveau projet .NET Core 3.x, pour cela :
|
||||
* lancez Visual Studio
|
||||
* créez un nouveau projet de type **Console App (.NET Core)** en C#
|
||||
|
||||
##### Installez EntityFramework Core
|
||||
Pour ce tutoriel, nous pouvons utiliser SqlServer comme *provider*.
|
||||
* Pour cela, cliquez droit sur le projet, puis sélectionnez *Gérer les packages NuGet...*.
|
||||
* Sous l'onglet *Parcourir*, dans la barre de recherche, rentrez *Microsoft.EntityFrameworkCore*
|
||||
* Sélectionnez le premier nuget dans sa version la plus récente et lancez l'installation.
|
||||
* Répétez les deux dernières opérations pour les packages :
|
||||
* *Microsoft.EntityFrameworkCore.Design*
|
||||
* *Microsoft.EntityFrameworkCore.SqlServer*
|
||||
* *Microsoft.EntityFrameworkCore.SqlServer.Design*
|
||||
|
||||
##### Créez un modèle
|
||||
* Ajoutez une nouvelle classe ```Nounours``` au projet.
|
||||
* Ajoutez le code suivant à cette classe :
|
||||
|
||||
```csharp title='Nounours.cs'
|
||||
using System;
|
||||
|
||||
namespace tutoRapideEFCore
|
||||
{
|
||||
class Nounours
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Nom { get; set; }
|
||||
public DateTime Naissance { get; set; }
|
||||
public int NbPoils { get; set; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Ajoutez une nouvelle classe ```NounoursContext``` qui servira de contexte à notre modèle.
|
||||
* Ajoutez le code suivante à cette classe :
|
||||
|
||||
```csharp title="NounoursContext.cs"
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace tutoRapideEFCore
|
||||
{
|
||||
class NounoursContext : DbContext
|
||||
{
|
||||
public DbSet<Nounours> Nounours { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=myFirstDatabase.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
##### Créez la base de données
|
||||
* Ouvrez la *Console du Gestionnaire de package*, 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 (*eg* si votre projet s'apelle **tutoRapideEFCore**) :
|
||||
```
|
||||
cd tutoRapideEFCore
|
||||
```
|
||||
* tapez ensuite les commandes suivantes :
|
||||
```
|
||||
dotnet ef migrations add myFirstMigration
|
||||
dotnet ef database update
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),
|
||||
|
||||
* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
|
||||
:::
|
||||
|
||||
##### Utilisez votre base de données via Entity Framework Core
|
||||
* Editez *Program.cs* et ajoutez le code suivant :
|
||||
```csharp title="Program.cs"
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca", Naissance = new DateTime(1977, 5, 27), NbPoils = 1234567 };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda", Naissance = new DateTime(1980, 5, 21), NbPoils = 3 };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok", Naissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };
|
||||
Nounours c3po = new Nounours { Nom = "C3PO", Naissance = new DateTime(1977, 5, 27), NbPoils = 0 };
|
||||
|
||||
using (var context = new NounoursContext())
|
||||
{
|
||||
// Crée des nounours et les insère dans la base
|
||||
Console.WriteLine("Creates and inserts new Nounours");
|
||||
context.Add(chewie);
|
||||
context.Add(yoda);
|
||||
context.Add(ewok);
|
||||
context.Add(c3po);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
```
|
||||
Maintenant, lorsque vous lancerez l'application, la base de données contiendra 3 nounours. La base crée automatiquement des identifiants pour chaque Nounours.
|
||||
* Editez *Program.cs* pour rajouter les lignes suivantes à la fin de la méthode ```Main``` :
|
||||
```csharp title="Program.cs"
|
||||
// Lit le premier nounours de la base dont le nom commence par 'e'
|
||||
Console.WriteLine("Creates and executes a query retrieving the first Nounours of the database whose name starts with an \"e\":");
|
||||
var eNounours = context.Nounours
|
||||
.Where(n => n.Nom.StartsWith("e"))
|
||||
.First();
|
||||
Console.WriteLine($"{eNounours.Nom} (born in {eNounours.Naissance.Year})");
|
||||
```
|
||||
Cette requête LINQ vous permet de lire le premier nounours de la base de nounours triés par ordre alphabétique de noms.
|
||||
Ceci nécessite de rajouter au-début du fichier *Program.cs* la ligne suivante :
|
||||
```csharp title="Program.cs"
|
||||
using System.Linq;
|
||||
```
|
||||
* Editez *Program.cs* pour rajouter les lignes suivantes à la fin de la méthode ```Main``` :
|
||||
```csharp title="Program.cs"
|
||||
// Met à jour le nom du second nounours de la base
|
||||
Console.WriteLine("Updates the name of the second nounours");
|
||||
eNounours.Nom = "Wicket";
|
||||
context.SaveChanges();
|
||||
```
|
||||
Cette partie du code montre comment mettre à jour un élément de la base de données.
|
||||
* Editez *Program.cs* pour rajouter les lignes suivantes à la fin de la méthode ```Main``` :
|
||||
```csharp title="Program.cs"
|
||||
// récupère le nounours qui n'en est pas un et le supprime de la base
|
||||
Console.WriteLine("Deletes one item from de database");
|
||||
var droid = context.Nounours
|
||||
.SingleOrDefault(n => n.Nom.Equals("C3PO"));
|
||||
context.Remove(droid);
|
||||
context.SaveChanges();
|
||||
```
|
||||
Cette partie du code montre comment supprimer un élément de la base de données.
|
||||
|
||||
Voici un récapitulatif du fichier *Program.cs* :
|
||||
```csharp title="Program.cs"
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace tutoRapideEFCore
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Nounours chewie = new Nounours { Nom = "Chewbacca", Naissance = new DateTime(1977, 5, 27), NbPoils = 1234567 };
|
||||
Nounours yoda = new Nounours { Nom = "Yoda", Naissance = new DateTime(1980, 5, 21), NbPoils = 3 };
|
||||
Nounours ewok = new Nounours { Nom = "Ewok", Naissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };
|
||||
Nounours c3po = new Nounours { Nom = "C3PO", Naissance = new DateTime(1977, 5, 27), NbPoils = 0 };
|
||||
|
||||
using (var context = new NounoursContext())
|
||||
{
|
||||
// Crée des nounours et les insère dans la base
|
||||
Console.WriteLine("Creates and inserts new Nounours");
|
||||
context.Add(chewie);
|
||||
context.Add(yoda);
|
||||
context.Add(ewok);
|
||||
context.Add(c3po);
|
||||
context.SaveChanges();
|
||||
|
||||
// Lit le premier nounours de la base dont le nom commence par 'e'
|
||||
Console.WriteLine("Creates and executes a query retrieving the first Nounours of the database whose name starts with an \"e\":");
|
||||
var eNounours = context.Nounours
|
||||
.Where(n => n.Nom.StartsWith("e"))
|
||||
.First();
|
||||
Console.WriteLine($"{eNounours.Nom} (born in {eNounours.Naissance.Year})");
|
||||
|
||||
// Met à jour le nom du second nounours de la base
|
||||
Console.WriteLine("Updates the name of the second nounours");
|
||||
eNounours.Nom = "Wicket";
|
||||
context.SaveChanges();
|
||||
|
||||
// récupère le nounours qui n'en est pas un et le supprime de la base
|
||||
Console.WriteLine("Deletes one item from de database");
|
||||
var droid = context.Nounours
|
||||
.SingleOrDefault(n => n.Nom.Equals("C3PO"));
|
||||
context.Remove(droid);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
* Exécutez votre application pour vérifier son bon fonctionnement.
|
||||
|
||||
##### Vérifiez le contenu de la base de données avec l'Explorateur d'objets SQL Server
|
||||
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
|
||||
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
|
||||
* Déployez dans l'*Explorateur d'objets SQL Server* :
|
||||
* *SQL Server*,
|
||||
* puis *(localdb)\MSSQLLocalDB ...*,
|
||||
* puis *Bases de données*
|
||||
* puis celle portant le nom de votre migration, dans mon cas : *myFirstDatabase.Nounours.mdf*
|
||||
* puis *Tables*
|
||||
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
|
||||
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|
||||
|
||||
|Id |Nom |Naissance |NbPoils
|
||||
|---|---|---|---
|
||||
|1|Chewbacca|27/05/1977 00:00:00|1234567
|
||||
|2|Yoda|21/05/1980 00:00:00|3
|
||||
|3|Wicket|25/05/1983 00:00:00|3456789
|
||||
|
||||
Vous pouvez constater que l'Ewok a bien été renommé Wicket, et que C3PO a bien été supprimé.
|
||||
Notez qu'il est également possible d'utiliser l'*Explorateur d'objets SQL Server* pour ajouter, modifier ou supprimer des données dans les tables.
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: Unit Tests avec xUnit
|
||||
---
|
||||
|
||||
# Unit Tests avec xUnit
|
||||
|
||||
Vous trouverez ici une vidéo de présentation des tests unitaires avec xUnit. Je ne montre pas tout, mais ça vous permettra de débuter avec :
|
||||
- [Fact] : des tests simples,
|
||||
- [Theory] + [InlineData] : des tests avec jeu de données (mais uniquement des constantes)
|
||||
- [Theory] + [MemberData] : des tests avec des jeux de données pouvant posséder des paramètres construits à la volée, dans la classe de tests ou dans une classe extérieure.
|
||||
|
||||
N'oubliez pas que vous pouvez trouver les exemples de cours ici sur les tests unitaires : [Unit Tests sample](https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core/src/branch/master/p05_More_dotNet/ex_026_001_xUnit_Fact)
|
||||
|
||||
<iframe allowfullscreen src='https://opencast.dsi.uca.fr/paella/ui/embed.html?id=4563de39-f30d-4080-ab8b-96e29a4e9ca7' width='640' height='480' frameborder='0' scrolling='no' marginwidth='0' marginheight='0' allowfullscreen='true' webkitallowfullscreen='true' mozallowfullscreen='true' ></iframe>
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# mchSamples C# .NET
|
||||
|
||||
## Tools to install
|
||||
|
||||
### Integrated Development Environment
|
||||
|
||||
These samples have been updated to .NET 6.0. You only need Visual Studio 2022 or Visual Studio 2019 tu run these samples.
|
||||
Visual Studio 2022 can be found here : <https://visualstudio.microsoft.com/fr/vs/>
|
||||
Visual Studio 2019 can be found here : <https://visualstudio.microsoft.com/fr/vs/older-downloads/>
|
||||
|
||||
### Docker image
|
||||
|
||||
If you need a docker image of .NET 6.0, you can use this one : mcr.microsoft.com/dotnet/sdk:6.0
|
||||
|
||||
### .NET 6.0 SDK and runtime
|
||||
|
||||
If you want to use the SDK and runtime of .NET 5.0 without using Visual Studio, you can find them here : <https://dotnet.microsoft.com/en-us/download/dotnet/6.0>
|
@ -1,139 +0,0 @@
|
||||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github');
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'C# .NET samples',
|
||||
tagline: 'Best samples ever',
|
||||
url: 'https://codefirst.iut.uca.fr',
|
||||
baseUrl: '/documentation/mchSamples_.NET/docusaurus/mchsamples-.net-core/',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
favicon: 'img/favicon.ico',
|
||||
trailingSlash: true,
|
||||
|
||||
// GitHub pages deployment config.
|
||||
// If you aren't using GitHub pages, you don't need these.
|
||||
organizationName: 'mch', // Usually your GitHub org/user name.
|
||||
projectName: 'mchsamples-.net-core', // Usually your repo name.
|
||||
|
||||
// Even if you don't use internalization, you can use this field to set useful
|
||||
// metadata like html lang. For example, if your site is Chinese, you may want
|
||||
// to replace "en" with "zh-Hans".
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
//locales: ['en','fr'],
|
||||
// localeConfigs: {
|
||||
// en: {
|
||||
// htmlLang: 'en-GB',
|
||||
// },
|
||||
// fr: {
|
||||
// direction: 'rtl'
|
||||
// }
|
||||
// }
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
// editUrl:
|
||||
// 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
|
||||
},
|
||||
blog: {
|
||||
showReadingTime: true,
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
// editUrl:
|
||||
// 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
navbar: {
|
||||
title: 'C# .NET samples',
|
||||
logo: {
|
||||
alt: 'My Site Logo',
|
||||
src: 'img/logo.svg',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'doc',
|
||||
docId: 'intro',
|
||||
position: 'left',
|
||||
label: 'Samples',
|
||||
},
|
||||
{to: '/blog', label: 'Blog', position: 'left'},
|
||||
{
|
||||
href: 'https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core',
|
||||
label: 'Code#0',
|
||||
position: 'right',
|
||||
},
|
||||
// {
|
||||
// type: 'localeDropdown',
|
||||
// position: 'left',
|
||||
// }
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{
|
||||
title: 'Docs',
|
||||
items: [
|
||||
{
|
||||
label: 'Tutorial',
|
||||
to: '/docs/intro',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'LinkedIn',
|
||||
href: 'https://www.linkedin.com/company/code-1st/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'More',
|
||||
items: [
|
||||
{
|
||||
label: 'Blog',
|
||||
to: '/blog',
|
||||
},
|
||||
{
|
||||
label: 'Code#0',
|
||||
href: 'https://codefirst.iut.uca.fr/git/mchSamples_.NET/mchsamples-.net-core',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
|
||||
},
|
||||
prism: {
|
||||
additionalLanguages: ['csharp'],
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "mon-cours",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.1.0",
|
||||
"@docusaurus/preset-classic": "2.1.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"mermaid": "^9.1.7",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.1.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
@ -1,61 +0,0 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const FeatureList = [
|
||||
{
|
||||
title: 'C# .NET Fundamentals',
|
||||
Svg: require('@site/static/img/csharp_original_logo_icon_146578.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Samples from basics (classes, events, collections) to fundamentals (LINQ, persistance)
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Entity Framework Core',
|
||||
Svg : require('@site/static/img/EFCore.svg').default,
|
||||
description: (
|
||||
<>
|
||||
All about using EF Core with the Code First approach to create and consume a local database.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Unit Tests',
|
||||
Svg: require('@site/static/img/xUnit.svg').default,
|
||||
description: (
|
||||
<>
|
||||
How to test your .NET code with xUnit?
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Feature({Svg, title, description}) {
|
||||
return (
|
||||
<div className={clsx('col col--4')}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomepageFeatures() {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
.features {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #852e2e;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary: #a06666;
|
||||
--ifm-color-primary-dark: #21af90;
|
||||
--ifm-color-primary-darker: #1fa588;
|
||||
--ifm-color-primary-darkest: #1a8870;
|
||||
--ifm-color-primary-light: #29d5b0;
|
||||
--ifm-color-primary-lighter: #32d8b4;
|
||||
--ifm-color-primary-lightest: #4fddbf;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import Layout from '@theme/Layout';
|
||||
import HomepageFeatures from '@site/src/components/HomepageFeatures';
|
||||
|
||||
import styles from './index.module.css';
|
||||
|
||||
function HomepageHeader() {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
return (
|
||||
<header className={clsx('hero hero--primary', styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
to="/docs/intro">
|
||||
Parse samples now 💻
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />">
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* CSS files with the .module.css suffix will be treated as CSS modules
|
||||
* and scoped locally.
|
||||
*/
|
||||
|
||||
.heroBanner {
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.heroBanner {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Markdown page example
|
||||
---
|
||||
|
||||
# Markdown page example
|
||||
|
||||
You don't need React to write simple standalone pages.
|
@ -1,15 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import mermaid from "mermaid";
|
||||
|
||||
mermaid.initialize({
|
||||
startOnLoad: true
|
||||
});
|
||||
|
||||
const Mermaid = ({ chart }) => {
|
||||
useEffect(() => {
|
||||
mermaid.contentLoaded();
|
||||
}, []);
|
||||
return <div className="mermaid">{chart}</div>;
|
||||
};
|
||||
|
||||
export default Mermaid;
|
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 864 B |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.6 KiB |
@ -0,0 +1,20 @@
|
||||
// ========================================================================
|
||||
//
|
||||
// Copyright (C) 2016-2017 MARC CHEVALDONNE
|
||||
// marc.chevaldonne.free.fr
|
||||
//
|
||||
// Module : LISEZ_MOI.txt
|
||||
// Author : Marc Chevaldonné
|
||||
// Creation date : 2017-05-16
|
||||
//
|
||||
// ========================================================================
|
||||
|
||||
|
||||
INSTALLATION DES OUTILS .NET CORE POUR VISUAL STUDIO 2015
|
||||
|
||||
Pour permettre l'utilisation des outils .NET Core dans Visual Studio 2015, j'ai installé :
|
||||
".NET Core tools Preview 2 for Visual Studio 2015"
|
||||
que j'ai trouvé sur la page suivante : https://www.microsoft.com/net/download/core
|
||||
le 16 mai 2017.
|
||||
|
||||
Ces outils vous permettront de générer et exécuter ces exemples, puis de créer vos projets projets .NET Core.
|
@ -1,14 +1,20 @@
|
||||
# mchSamples C# .NET
|
||||
# mchSamples .NET Core
|
||||
|
||||
## Tools to install
|
||||
|
||||
### Integrated Development Environment
|
||||
These samples have been updated to .NET 6.0. You only need Visual Studio 2022 or Visual Studio 2019 tu run these samples.
|
||||
Visual Studio 2022 can be found here : https://visualstudio.microsoft.com/fr/vs/
|
||||
Visual Studio 2019 can be found here : https://visualstudio.microsoft.com/fr/vs/older-downloads/
|
||||
### if you want to run .NET Core 3.1.100 (recommended)
|
||||
* .NET Core 3.1.100 installer: https://dotnet.microsoft.com/download
|
||||
* direct link for windows: https://download.visualstudio.microsoft.com/download/pr/639f7cfa-84f8-48e8-b6c9-82634314e28f/8eb04e1b5f34df0c840c1bffa363c101/dotnet-sdk-3.1.100-win-x64.exe
|
||||
* direct link for maxosx: https://download.visualstudio.microsoft.com/download/pr/787e81f1-f0da-4e3b-a989-8a199132ed8c/61a8dba81fbf2b3d533562d7b96443ec/dotnet-sdk-3.1.100-osx-x64.pkg
|
||||
|
||||
### Docker image
|
||||
If you need a docker image of .NET 6.0, you can use this one : mcr.microsoft.com/dotnet/sdk:6.0
|
||||
|
||||
### .NET 6.0 SDK and runtime
|
||||
If you want to use the SDK and runtime of .NET 6.0 without using Visual Studio, you can find them here : https://dotnet.microsoft.com/en-us/download/dotnet/6.0
|
||||
### if you want to run .NET Core 3.0
|
||||
* .NET Core 3.0.1: https://dotnet.microsoft.com/download/dotnet-core/3.0
|
||||
* direct link to SDK:
|
||||
* for MacOSX: https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.0.101-macos-x64-installer
|
||||
* for Windows: https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.0.101-windows-x64-installer
|
||||
|
||||
### if you want to run .NET Core 2.2
|
||||
Here are all the different Tools to be installed with Visual Studio 2019, in order to be able to use .NET Core 2.2:
|
||||
* .NET Core 2.2.6: https://dotnet.microsoft.com/download/dotnet-core/2.2
|
||||
* direct link to SDK: https://dotnet.microsoft.com/download/thank-you/dotnet-sdk-2.2.301-windows-x64-installer
|
||||
* direct link to ASP.NET Core & .NET Core installers for Windows: https://dotnet.microsoft.com/download/thank-you/dotnet-runtime-2.2.6-windows-hosting-bundle-installer
|
@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssemblyName>ex_003_001_Main</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>ex_003_001_Main</PackageId>
|
||||
<RuntimeFrameworkVersion>6.0.6</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<AssemblyName>ex_003_001_Main</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>ex_003_001_Main</PackageId>
|
||||
<RuntimeFrameworkVersion>3.0.1</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssemblyName>ex_003_002_Main_HelloWorld</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>ex_003_002_Main_HelloWorld</PackageId>
|
||||
<RuntimeFrameworkVersion>6.0.6</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<AssemblyName>ex_003_002_Main_HelloWorld</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>ex_003_002_Main_HelloWorld</PackageId>
|
||||
<RuntimeFrameworkVersion>3.0.1</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
</PropertyGroup>
|
||||
</Project>
|