Merge branch 'EFCore3_Reforged'

pull/1/head
Marc CHEVALDONNE 2 years ago
commit 882cfde4ff

@ -13,8 +13,13 @@ build:
artifacts:
paths:
- $CI_PROJECT_DIR/build/release
#test:
# stage: test
# script:
# - dotnet test --no-restore
test:
stage: test
tags:
- docker
script:
- cd ./p08_BDD_EntityFramework/ex_041_004_UnitTests_w_InMemory
- dotnet test --no-restore
- cd ../..
- cd ./p08_BDD_EntityFramework/ex_041_004_UnitTests_w_SQLiteInMemory
- dotnet test --no-restore

@ -90,17 +90,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap039_LINQ_to_XML", "chap
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap040_LINQ_to_Json", "chap040_LINQ_to_Json", "{3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "p08_BDD", "p08_BDD", "{65DF0C9A-4F06-4361-8DD9-E8360B6325CA}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "p08_BDD_EntityFramework", "p08_BDD_EntityFramework", "{65DF0C9A-4F06-4361-8DD9-E8360B6325CA}"
ProjectSection(SolutionItems) = preProject
p08_BDD_EntityFramework\ReadMe.md = p08_BDD_EntityFramework\ReadMe.md
p08_BDD_EntityFramework\temp.md = p08_BDD_EntityFramework\temp.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap042_EntityFramework", "chap042_EntityFramework", "{ED951AD1-2EB1-49CC-9DBE-E3360E11525B}"
ProjectSection(SolutionItems) = preProject
temp.md = temp.md
p08_BDD_EntityFramework\Relationships.md = p08_BDD_EntityFramework\Relationships.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. EF CodeFirst approach", "01. EF CodeFirst approach", "{D2D113F6-9444-4F75-959F-8761054F1AEE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Initialization Strategy and Seeding Data with CodeFirst", "02. Initialization Strategy and Seeding Data with CodeFirst", "{41B79FDB-6371-41E1-B9A3-73A9A209E906}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03. Entity relationships with CodeFirst", "03. Entity relationships with CodeFirst", "{593200F9-6D14-43BC-9289-8BB75FAC6552}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04. Inheritance with CodeFirst", "04. Inheritance with CodeFirst", "{663523E4-3B60-4534-876F-8BD77110E6D8}"
@ -412,22 +412,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_039_002_LINQ_to_XML", "e
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_040_001_LINQ_to_Json", "ex_040_001_LINQ_to_Json\ex_040_001_LINQ_to_Json.csproj", "{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_001_EF_CodeFirst_conventions", "ex_042_001_EF_CodeFirst_conventions\ex_042_001_EF_CodeFirst_conventions.csproj", "{515F8C64-9070-4110-B3E0-092379DF6444}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_002_EF_CodeFirst_data_annotations", "ex_042_002_EF_CodeFirst_data_annotations\ex_042_002_EF_CodeFirst_data_annotations.csproj", "{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CodeFirst_Fluent_API", "ex_042_003_EF_CodeFirst_Fluent_API\ex_042_003_EF_CodeFirst_Fluent_API.csproj", "{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_EF_CF_InitializationStrategy", "ex_042_004_EF_CF_InitializationStrategy\ex_042_004_EF_CF_InitializationStrategy.csproj", "{C25760E4-A043-4BB9-8A8D-2907CCB810FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_005_EF_CF_Seeding_Data", "ex_042_005_EF_CF_Seeding_Data\ex_042_005_EF_CF_Seeding_Data.csproj", "{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_006_EF_CF_One_to_One", "ex_042_006_EF_CF_One_to_One\ex_042_006_EF_CF_One_to_One.csproj", "{0770D7BB-F7FA-4E44-900C-F72F33110AF9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_007_EF_CF_One_to_One_FluentAPI", "ex_042_007_EF_CF_One_to_One_FluentAPI\ex_042_007_EF_CF_One_to_One_FluentAPI.csproj", "{4D079A62-B303-4900-BEC1-994BC26BCCA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_008_EF_CF_One_to_Many", "ex_042_008_EF_CF_One_to_Many\ex_042_008_EF_CF_One_to_Many.csproj", "{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_009_EF_CF_One_to_Many_FluentAPI", "ex_042_009_EF_CF_One_to_Many_FluentAPI\ex_042_009_EF_CF_One_to_Many_FluentAPI.csproj", "{2C53F069-6E87-4A36-8915-E0219EBA8BA7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_010_EF_CF_Many_to_Many", "ex_042_010_EF_CF_Many_to_Many\ex_042_010_EF_CF_Many_to_Many.csproj", "{C6EF99F3-959D-4096-8ABE-140596DD14BD}"
@ -534,6 +518,69 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_050_010_Threading_en_UWP", "ex_050_010_Threading_en_UWP\ex_050_010_Threading_en_UWP.csproj", "{0D922BCD-7C0F-43FF-A015-F547716B8284}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_052_006_problématique_6_UWP", "ex_052_006_problématique_6_UWP\ex_052_006_problématique_6_UWP.csproj", "{BCE167F6-6323-46FC-A112-D9941A75815A}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap041_EF_Fundamentals", "chap041_EF_Fundamentals", "{56C5A51B-16E9-4F93-9C32-8F91710391E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_LISEZ-MOI_ !!", "_LISEZ-MOI_ !!", "{AB06CC3B-6D82-48D1-89FC-14C96C77FE7F}"
ProjectSection(SolutionItems) = preProject
p08_BDD_EntityFramework\ReadMe.md = p08_BDD_EntityFramework\ReadMe.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_001_ConnectionStrings", "p08_BDD_EntityFramework\ex_041_001_ConnectionStrings\ex_041_001_ConnectionStrings.csproj", "{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_004_TestingInMemory", "p08_BDD_EntityFramework\ex_041_004_TestingInMemory\ex_041_004_TestingInMemory.csproj", "{166633F1-D1B3-4038-B015-B238A14C97C3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_004_UnitTests_w_InMemory", "p08_BDD_EntityFramework\ex_041_004_UnitTests_w_InMemory\ex_041_004_UnitTests_w_InMemory.csproj", "{8C4BC92E-2E7D-4447-805F-0BCAF986022E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_004_ConsoleTests_w_SqlServer", "p08_BDD_EntityFramework\ex_041_004_ConsoleTests_w_SqlServer\ex_041_004_ConsoleTests_w_SqlServer.csproj", "{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_004_UnitTests_w_SQLiteInMemory", "p08_BDD_EntityFramework\ex_041_004_UnitTests_w_SQLiteInMemory\ex_041_004_UnitTests_w_SQLiteInMemory.csproj", "{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap042_EF_Creating_Model", "chap042_EF_Creating_Model", "{55E00151-58A6-4E7D-9457-0BB8213B82F5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. EF CodeFirst approach", "01. EF CodeFirst approach", "{FC04E822-7560-4C80-9E59-C03DB40E9F17}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_001_EF_CF_conventions", "p08_BDD_EntityFramework\ex_042_001_EF_CF_conventions\ex_042_001_EF_CF_conventions.csproj", "{1566457E-6D28-412C-86A3-95E8821B2DDD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_002_EF_CF_data_annotations", "p08_BDD_EntityFramework\ex_042_002_EF_CF_data_annotations\ex_042_002_EF_CF_data_annotations.csproj", "{568486B5-85E4-4F86-B86C-A373B5F471FD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CF_Fluent_API", "p08_BDD_EntityFramework\ex_042_003_EF_CF_Fluent_API\ex_042_003_EF_CF_Fluent_API.csproj", "{9D444878-F974-4883-9023-1A46E80ADFF1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Keys and value generation", "02. Keys and value generation", "{5B333C02-67B7-4A4C-AA58-2710C183292B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_Keys_conventions", "p08_BDD_EntityFramework\ex_042_004_Keys_conventions\ex_042_004_Keys_conventions.csproj", "{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_005_Keys_data_annotations", "p08_BDD_EntityFramework\ex_042_005_Keys_data_annotations\ex_042_005_Keys_data_annotations.csproj", "{27725449-27B2-47ED-A2B3-738851E55C64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_006_Keys_FluentAPI", "p08_BDD_EntityFramework\ex_042_006_Keys_FluentAPI\ex_042_006_Keys_FluentAPI.csproj", "{64EA0021-231A-421F-A616-3973C0106E99}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_007_ValueGeneration", "p08_BDD_EntityFramework\ex_042_007_ValueGeneration\ex_042_007_ValueGeneration.csproj", "{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03. Data Seeding", "03. Data Seeding", "{78374D80-5BE7-425D-BE62-AD8F26491112}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_008_DataSeeding_before_EF2.1", "p08_BDD_EntityFramework\ex_042_008_DataSeeding_before_EF2.1\ex_042_008_DataSeeding_before_EF2.1.csproj", "{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_041_004_ConsoleTests_w_SQLite", "p08_BDD_EntityFramework\ex_041_004_ConsoleTests_w_SQLite\ex_041_004_ConsoleTests_w_SQLite.csproj", "{2249C4B0-563D-44C8-AAA5-C8366A8F509C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_009_DataSeeding", "p08_BDD_EntityFramework\ex_042_009_DataSeeding\ex_042_009_DataSeeding.csproj", "{B5CEC904-1475-4215-A20A-01BDD219AE25}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04. Relationships", "04. Relationships", "{C7672736-AA73-4B7E-B5C6-A7A984216372}"
ProjectSection(SolutionItems) = preProject
p08_BDD_EntityFramework\Relationships.md = p08_BDD_EntityFramework\Relationships.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_010_SinglePropertyNavigation_conventions", "p08_BDD_EntityFramework\ex_042_010_SinglePropertyNavigation_conventions\ex_042_010_SinglePropertyNavigation_conventions.csproj", "{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_011_SinglePropertyNavigation_FluentAPI", "p08_BDD_EntityFramework\ex_042_011_SinglePropertyNavigation_FluentAPI\ex_042_011_SinglePropertyNavigation_FluentAPI.csproj", "{CCC912CB-2962-441E-AB6B-97B0CB32AF11}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_012_OneToOne_conventions", "p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions\ex_042_012_OneToOne_conventions.csproj", "{762C349D-5685-43FA-A077-2F3BDD07C898}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_013_OneToOne_FluentAPI", "p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI\ex_042_013_OneToOne_FluentAPI.csproj", "{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_014_OneToMany_dataAnnotations", "p08_BDD_EntityFramework\ex_042_014_OneToMany_dataAnnotations\ex_042_014_OneToMany_dataAnnotations.csproj", "{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_015_OneToMany_conventions", "p08_BDD_EntityFramework\ex_042_015_OneToMany_conventions\ex_042_015_OneToMany_conventions.csproj", "{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_016_OneToMany_FluentAPI", "p08_BDD_EntityFramework\ex_042_016_OneToMany_FluentAPI\ex_042_016_OneToMany_FluentAPI.csproj", "{9C2D4443-635F-4BA7-9870-9705BAD534EF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -3441,166 +3488,6 @@ Global
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x64.Build.0 = Release|Any CPU
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.ActiveCfg = Release|Any CPU
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.Build.0 = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|Any CPU.Build.0 = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM.ActiveCfg = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM.Build.0 = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM64.Build.0 = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x64.ActiveCfg = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x64.Build.0 = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x86.ActiveCfg = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x86.Build.0 = Debug|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|Any CPU.ActiveCfg = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|Any CPU.Build.0 = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM.ActiveCfg = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM.Build.0 = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM64.ActiveCfg = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM64.Build.0 = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|x64.ActiveCfg = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|x64.Build.0 = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|x86.ActiveCfg = Release|Any CPU
{515F8C64-9070-4110-B3E0-092379DF6444}.Release|x86.Build.0 = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM.Build.0 = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM64.Build.0 = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x64.ActiveCfg = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x64.Build.0 = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x86.ActiveCfg = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x86.Build.0 = Debug|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|Any CPU.Build.0 = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM.ActiveCfg = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM.Build.0 = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM64.ActiveCfg = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM64.Build.0 = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x64.ActiveCfg = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x64.Build.0 = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x86.ActiveCfg = Release|Any CPU
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x86.Build.0 = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM.Build.0 = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM64.Build.0 = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x64.ActiveCfg = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x64.Build.0 = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x86.ActiveCfg = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x86.Build.0 = Debug|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|Any CPU.Build.0 = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM.ActiveCfg = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM.Build.0 = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM64.ActiveCfg = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM64.Build.0 = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x64.ActiveCfg = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x64.Build.0 = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x86.ActiveCfg = Release|Any CPU
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x86.Build.0 = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|ARM.Build.0 = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|ARM64.Build.0 = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|x64.ActiveCfg = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|x64.Build.0 = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|x86.ActiveCfg = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|x86.Build.0 = Debug|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|Any CPU.Build.0 = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|ARM.ActiveCfg = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|ARM.Build.0 = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|ARM64.ActiveCfg = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|ARM64.Build.0 = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|x64.ActiveCfg = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|x64.Build.0 = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|x86.ActiveCfg = Release|Any CPU
{C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Release|x86.Build.0 = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|ARM.Build.0 = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|ARM64.Build.0 = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|x64.ActiveCfg = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|x64.Build.0 = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|x86.ActiveCfg = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Debug|x86.Build.0 = Debug|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|Any CPU.Build.0 = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|ARM.ActiveCfg = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|ARM.Build.0 = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|ARM64.ActiveCfg = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|ARM64.Build.0 = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|x64.ActiveCfg = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|x64.Build.0 = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|x86.ActiveCfg = Release|Any CPU
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}.Release|x86.Build.0 = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|ARM.Build.0 = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|ARM64.Build.0 = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|x64.ActiveCfg = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|x64.Build.0 = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|x86.ActiveCfg = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Debug|x86.Build.0 = Debug|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|Any CPU.Build.0 = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|ARM.ActiveCfg = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|ARM.Build.0 = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|ARM64.ActiveCfg = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|ARM64.Build.0 = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|x64.ActiveCfg = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|x64.Build.0 = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|x86.ActiveCfg = Release|Any CPU
{0770D7BB-F7FA-4E44-900C-F72F33110AF9}.Release|x86.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|ARM64.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x64.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x64.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x86.ActiveCfg = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Debug|x86.Build.0 = Debug|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|Any CPU.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM64.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|ARM64.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x64.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x64.Build.0 = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x86.ActiveCfg = Release|Any CPU
{4D079A62-B303-4900-BEC1-994BC26BCCA2}.Release|x86.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|ARM.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|ARM.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|ARM64.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|x64.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|x64.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|x86.ActiveCfg = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Debug|x86.Build.0 = Debug|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|Any CPU.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|ARM.ActiveCfg = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|ARM.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|ARM64.ActiveCfg = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|ARM64.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|x64.ActiveCfg = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|x64.Build.0 = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|x86.ActiveCfg = Release|Any CPU
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03}.Release|x86.Build.0 = Release|Any CPU
{2C53F069-6E87-4A36-8915-E0219EBA8BA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C53F069-6E87-4A36-8915-E0219EBA8BA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C53F069-6E87-4A36-8915-E0219EBA8BA7}.Debug|ARM.ActiveCfg = Debug|Any CPU
@ -4481,6 +4368,446 @@ Global
{BCE167F6-6323-46FC-A112-D9941A75815A}.Release|x64.Build.0 = Release|x64
{BCE167F6-6323-46FC-A112-D9941A75815A}.Release|x86.ActiveCfg = Release|x86
{BCE167F6-6323-46FC-A112-D9941A75815A}.Release|x86.Build.0 = Release|x86
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|ARM.Build.0 = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|ARM64.Build.0 = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|x64.ActiveCfg = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|x64.Build.0 = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|x86.ActiveCfg = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Debug|x86.Build.0 = Debug|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|Any CPU.Build.0 = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|ARM.ActiveCfg = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|ARM.Build.0 = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|ARM64.ActiveCfg = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|ARM64.Build.0 = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|x64.ActiveCfg = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|x64.Build.0 = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|x86.ActiveCfg = Release|Any CPU
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C}.Release|x86.Build.0 = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|ARM.ActiveCfg = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|ARM.Build.0 = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|ARM64.Build.0 = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|x64.ActiveCfg = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|x64.Build.0 = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|x86.ActiveCfg = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Debug|x86.Build.0 = Debug|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|Any CPU.Build.0 = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|ARM.ActiveCfg = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|ARM.Build.0 = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|ARM64.ActiveCfg = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|ARM64.Build.0 = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|x64.ActiveCfg = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|x64.Build.0 = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|x86.ActiveCfg = Release|Any CPU
{166633F1-D1B3-4038-B015-B238A14C97C3}.Release|x86.Build.0 = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|ARM.Build.0 = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|ARM64.Build.0 = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|x64.ActiveCfg = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|x64.Build.0 = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|x86.ActiveCfg = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Debug|x86.Build.0 = Debug|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|Any CPU.Build.0 = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|ARM.ActiveCfg = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|ARM.Build.0 = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|ARM64.ActiveCfg = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|ARM64.Build.0 = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|x64.ActiveCfg = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|x64.Build.0 = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|x86.ActiveCfg = Release|Any CPU
{8C4BC92E-2E7D-4447-805F-0BCAF986022E}.Release|x86.Build.0 = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|ARM.Build.0 = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|ARM64.Build.0 = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|x64.ActiveCfg = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|x64.Build.0 = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|x86.ActiveCfg = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Debug|x86.Build.0 = Debug|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|Any CPU.Build.0 = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|ARM.ActiveCfg = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|ARM.Build.0 = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|ARM64.ActiveCfg = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|ARM64.Build.0 = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|x64.ActiveCfg = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|x64.Build.0 = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|x86.ActiveCfg = Release|Any CPU
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C}.Release|x86.Build.0 = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|ARM.Build.0 = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|ARM64.Build.0 = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|x64.ActiveCfg = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|x64.Build.0 = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|x86.ActiveCfg = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Debug|x86.Build.0 = Debug|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|Any CPU.Build.0 = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|ARM.ActiveCfg = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|ARM.Build.0 = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|ARM64.ActiveCfg = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|ARM64.Build.0 = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|x64.ActiveCfg = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|x64.Build.0 = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|x86.ActiveCfg = Release|Any CPU
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D}.Release|x86.Build.0 = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|ARM.ActiveCfg = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|ARM.Build.0 = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|ARM64.Build.0 = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|x64.ActiveCfg = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|x64.Build.0 = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|x86.ActiveCfg = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Debug|x86.Build.0 = Debug|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|Any CPU.Build.0 = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|ARM.ActiveCfg = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|ARM.Build.0 = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|ARM64.ActiveCfg = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|ARM64.Build.0 = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|x64.ActiveCfg = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|x64.Build.0 = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|x86.ActiveCfg = Release|Any CPU
{1566457E-6D28-412C-86A3-95E8821B2DDD}.Release|x86.Build.0 = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|ARM.ActiveCfg = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|ARM.Build.0 = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|ARM64.Build.0 = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|x64.ActiveCfg = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|x64.Build.0 = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|x86.ActiveCfg = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Debug|x86.Build.0 = Debug|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|Any CPU.Build.0 = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|ARM.ActiveCfg = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|ARM.Build.0 = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|ARM64.ActiveCfg = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|ARM64.Build.0 = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|x64.ActiveCfg = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|x64.Build.0 = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|x86.ActiveCfg = Release|Any CPU
{568486B5-85E4-4F86-B86C-A373B5F471FD}.Release|x86.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|ARM.Build.0 = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|ARM64.Build.0 = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|x64.ActiveCfg = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|x64.Build.0 = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|x86.ActiveCfg = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Debug|x86.Build.0 = Debug|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|Any CPU.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|ARM.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|ARM.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|ARM64.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|ARM64.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x64.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x64.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x86.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x86.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM64.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x64.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x64.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x86.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x86.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|Any CPU.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM64.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM64.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x64.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x64.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x86.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x86.Build.0 = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|ARM.ActiveCfg = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|ARM.Build.0 = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|ARM64.Build.0 = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|x64.ActiveCfg = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|x64.Build.0 = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|x86.ActiveCfg = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Debug|x86.Build.0 = Debug|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|Any CPU.Build.0 = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|ARM.ActiveCfg = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|ARM.Build.0 = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|ARM64.ActiveCfg = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|ARM64.Build.0 = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|x64.ActiveCfg = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|x64.Build.0 = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|x86.ActiveCfg = Release|Any CPU
{27725449-27B2-47ED-A2B3-738851E55C64}.Release|x86.Build.0 = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM.ActiveCfg = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM.Build.0 = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM64.Build.0 = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|x64.ActiveCfg = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|x64.Build.0 = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|x86.ActiveCfg = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Debug|x86.Build.0 = Debug|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|Any CPU.Build.0 = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM.ActiveCfg = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM.Build.0 = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM64.ActiveCfg = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM64.Build.0 = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|x64.ActiveCfg = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|x64.Build.0 = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|x86.ActiveCfg = Release|Any CPU
{64EA0021-231A-421F-A616-3973C0106E99}.Release|x86.Build.0 = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM.Build.0 = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM64.Build.0 = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x64.ActiveCfg = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x64.Build.0 = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x86.ActiveCfg = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x86.Build.0 = Debug|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|Any CPU.Build.0 = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM.ActiveCfg = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM.Build.0 = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM64.ActiveCfg = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM64.Build.0 = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x64.ActiveCfg = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x64.Build.0 = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x86.ActiveCfg = Release|Any CPU
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x86.Build.0 = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|ARM.ActiveCfg = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|ARM.Build.0 = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|ARM64.Build.0 = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|x64.ActiveCfg = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|x64.Build.0 = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|x86.ActiveCfg = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Debug|x86.Build.0 = Debug|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|Any CPU.Build.0 = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|ARM.ActiveCfg = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|ARM.Build.0 = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|ARM64.ActiveCfg = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|ARM64.Build.0 = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|x64.ActiveCfg = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|x64.Build.0 = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|x86.ActiveCfg = Release|Any CPU
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5}.Release|x86.Build.0 = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|ARM.Build.0 = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|ARM64.Build.0 = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|x64.ActiveCfg = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|x64.Build.0 = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|x86.ActiveCfg = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Debug|x86.Build.0 = Debug|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|Any CPU.Build.0 = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|ARM.ActiveCfg = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|ARM.Build.0 = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|ARM64.ActiveCfg = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|ARM64.Build.0 = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|x64.ActiveCfg = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|x64.Build.0 = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|x86.ActiveCfg = Release|Any CPU
{2249C4B0-563D-44C8-AAA5-C8366A8F509C}.Release|x86.Build.0 = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|ARM.ActiveCfg = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|ARM.Build.0 = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|ARM64.Build.0 = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|x64.ActiveCfg = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|x64.Build.0 = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|x86.ActiveCfg = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Debug|x86.Build.0 = Debug|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|Any CPU.Build.0 = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|ARM.ActiveCfg = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|ARM.Build.0 = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|ARM64.ActiveCfg = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|ARM64.Build.0 = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|x64.ActiveCfg = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|x64.Build.0 = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|x86.ActiveCfg = Release|Any CPU
{B5CEC904-1475-4215-A20A-01BDD219AE25}.Release|x86.Build.0 = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|ARM.Build.0 = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|ARM64.Build.0 = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|x64.ActiveCfg = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|x64.Build.0 = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|x86.ActiveCfg = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Debug|x86.Build.0 = Debug|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|Any CPU.Build.0 = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|ARM.ActiveCfg = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|ARM.Build.0 = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|ARM64.ActiveCfg = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|ARM64.Build.0 = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|x64.ActiveCfg = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|x64.Build.0 = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|x86.ActiveCfg = Release|Any CPU
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93}.Release|x86.Build.0 = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|ARM.ActiveCfg = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|ARM.Build.0 = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|ARM64.Build.0 = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|x64.ActiveCfg = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|x64.Build.0 = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|x86.ActiveCfg = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Debug|x86.Build.0 = Debug|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|Any CPU.Build.0 = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|ARM.ActiveCfg = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|ARM.Build.0 = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|ARM64.ActiveCfg = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|ARM64.Build.0 = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|x64.ActiveCfg = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|x64.Build.0 = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|x86.ActiveCfg = Release|Any CPU
{CCC912CB-2962-441E-AB6B-97B0CB32AF11}.Release|x86.Build.0 = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|Any CPU.Build.0 = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|ARM.ActiveCfg = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|ARM.Build.0 = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|ARM64.Build.0 = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|x64.ActiveCfg = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|x64.Build.0 = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|x86.ActiveCfg = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Debug|x86.Build.0 = Debug|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|Any CPU.ActiveCfg = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|Any CPU.Build.0 = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|ARM.ActiveCfg = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|ARM.Build.0 = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|ARM64.ActiveCfg = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|ARM64.Build.0 = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|x64.ActiveCfg = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|x64.Build.0 = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|x86.ActiveCfg = Release|Any CPU
{762C349D-5685-43FA-A077-2F3BDD07C898}.Release|x86.Build.0 = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|ARM.ActiveCfg = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|ARM.Build.0 = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|ARM64.Build.0 = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|x64.ActiveCfg = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|x64.Build.0 = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|x86.ActiveCfg = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Debug|x86.Build.0 = Debug|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|Any CPU.Build.0 = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|ARM.ActiveCfg = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|ARM.Build.0 = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|ARM64.ActiveCfg = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|ARM64.Build.0 = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|x64.ActiveCfg = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|x64.Build.0 = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|x86.ActiveCfg = Release|Any CPU
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}.Release|x86.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|ARM.Build.0 = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|ARM64.Build.0 = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|x64.ActiveCfg = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|x64.Build.0 = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|x86.ActiveCfg = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Debug|x86.Build.0 = Debug|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|Any CPU.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|ARM.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|ARM.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|ARM64.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|ARM64.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x64.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x64.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x86.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x86.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM64.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x64.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x64.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x86.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x86.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|Any CPU.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM64.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM64.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x64.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x64.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x86.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x86.Build.0 = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM.Build.0 = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM64.Build.0 = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x64.ActiveCfg = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x64.Build.0 = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x86.ActiveCfg = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x86.Build.0 = Debug|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|Any CPU.Build.0 = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM.ActiveCfg = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM.Build.0 = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM64.ActiveCfg = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM64.Build.0 = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x64.ActiveCfg = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x64.Build.0 = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x86.ActiveCfg = Release|Any CPU
{9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -4523,8 +4850,6 @@ Global
{13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5} = {1BCDE8FE-64B4-4C41-9323-81BB09EFD7CD}
{3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0} = {1BCDE8FE-64B4-4C41-9323-81BB09EFD7CD}
{ED951AD1-2EB1-49CC-9DBE-E3360E11525B} = {65DF0C9A-4F06-4361-8DD9-E8360B6325CA}
{D2D113F6-9444-4F75-959F-8761054F1AEE} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B}
{41B79FDB-6371-41E1-B9A3-73A9A209E906} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B}
{593200F9-6D14-43BC-9289-8BB75FAC6552} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B}
{663523E4-3B60-4534-876F-8BD77110E6D8} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B}
{01C1826E-5E4F-4220-A809-E99A82D77F4A} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B}
@ -4678,14 +5003,6 @@ Global
{CF1AE501-3518-45CC-94AA-D6DE047EA022} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5}
{26400B99-2FB6-4B5F-BE2B-D15124F2B51D} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5}
{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3} = {3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0}
{515F8C64-9070-4110-B3E0-092379DF6444} = {D2D113F6-9444-4F75-959F-8761054F1AEE}
{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC} = {D2D113F6-9444-4F75-959F-8761054F1AEE}
{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC} = {D2D113F6-9444-4F75-959F-8761054F1AEE}
{C25760E4-A043-4BB9-8A8D-2907CCB810FA} = {41B79FDB-6371-41E1-B9A3-73A9A209E906}
{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302} = {41B79FDB-6371-41E1-B9A3-73A9A209E906}
{0770D7BB-F7FA-4E44-900C-F72F33110AF9} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{4D079A62-B303-4900-BEC1-994BC26BCCA2} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{91FD20B6-250C-437B-9FBE-DA4D2B87DE03} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{2C53F069-6E87-4A36-8915-E0219EBA8BA7} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{C6EF99F3-959D-4096-8ABE-140596DD14BD} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{D9BE7F40-ADCE-415B-AF36-8D9B40B6DB7F} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
@ -4736,6 +5053,35 @@ Global
{A14CDD6E-3E6E-4E7A-9277-3F00C0F658FD} = {593200F9-6D14-43BC-9289-8BB75FAC6552}
{0D922BCD-7C0F-43FF-A015-F547716B8284} = {BF2B43D0-95C2-402B-92C3-D2464C117A6E}
{BCE167F6-6323-46FC-A112-D9941A75815A} = {7FDA2D7D-5385-485D-B8B5-60727583D6ED}
{56C5A51B-16E9-4F93-9C32-8F91710391E8} = {65DF0C9A-4F06-4361-8DD9-E8360B6325CA}
{AB06CC3B-6D82-48D1-89FC-14C96C77FE7F} = {65DF0C9A-4F06-4361-8DD9-E8360B6325CA}
{097B0926-6663-4B7E-BBBE-DAA7EE278C7C} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{166633F1-D1B3-4038-B015-B238A14C97C3} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{8C4BC92E-2E7D-4447-805F-0BCAF986022E} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{4AE5E57F-2D9B-40BD-B1C3-B39AE756172C} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{2BE75812-1DC2-4E9A-8D99-99456F11AB0D} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{55E00151-58A6-4E7D-9457-0BB8213B82F5} = {65DF0C9A-4F06-4361-8DD9-E8360B6325CA}
{FC04E822-7560-4C80-9E59-C03DB40E9F17} = {55E00151-58A6-4E7D-9457-0BB8213B82F5}
{1566457E-6D28-412C-86A3-95E8821B2DDD} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{568486B5-85E4-4F86-B86C-A373B5F471FD} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{9D444878-F974-4883-9023-1A46E80ADFF1} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{5B333C02-67B7-4A4C-AA58-2710C183292B} = {55E00151-58A6-4E7D-9457-0BB8213B82F5}
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{27725449-27B2-47ED-A2B3-738851E55C64} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{64EA0021-231A-421F-A616-3973C0106E99} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{DA7ADAF3-34FF-4B97-8306-EF490A7A713A} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{78374D80-5BE7-425D-BE62-AD8F26491112} = {55E00151-58A6-4E7D-9457-0BB8213B82F5}
{26B0F58C-3373-4965-A00A-FB9F9AA2DFC5} = {78374D80-5BE7-425D-BE62-AD8F26491112}
{2249C4B0-563D-44C8-AAA5-C8366A8F509C} = {56C5A51B-16E9-4F93-9C32-8F91710391E8}
{B5CEC904-1475-4215-A20A-01BDD219AE25} = {78374D80-5BE7-425D-BE62-AD8F26491112}
{C7672736-AA73-4B7E-B5C6-A7A984216372} = {55E00151-58A6-4E7D-9457-0BB8213B82F5}
{0D750BE3-0B5C-433C-A3A2-B70F390B4E93} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{CCC912CB-2962-441E-AB6B-97B0CB32AF11} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{762C349D-5685-43FA-A077-2F3BDD07C898} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{9C2D4443-635F-4BA7-9870-9705BAD534EF} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E}

@ -1,103 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
namespace ex_042_001_EF_CodeFirst_conventions
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// 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 va utiliser la classe POCO Nounours pour créer une table dans la base de données, lorsque le DbSet va être créé (nous verrons ça dans la classe NounoursDBEntities).
///
/// 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.
/// </summary>
public class Nounours
{
public int ID
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,36 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_001_EF_CodeFirst_conventions
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
///
/// Lorsque nous utiliserons cette classe, le programme cherchera la chaîne de connection (connectionString) avec le même nom que celui passé
/// dans le constructeur de DbContext (ici NounoursDBContext). Il cherche la connectionString à différents endroits et en particulier ici
/// dans App.config.
/// Si cette table n'existe pas, il va la créer automatiquement. Ceci est configurable et sera présenté dans les exemples suivants.
/// </summary>
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Initial Catalog=ex_042_001_EF_CodeFirst_conventions;Integrated Security=SSPI;AttachDBFileName=|DataDirectory|\ex_042_001_EF_CodeFirst_conventions.mdf");
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_001_EF_CodeFirst_conventions.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -1,124 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
using System.Linq;
using static System.Console;
namespace ex_042_001_EF_CodeFirst_conventions
{
/// <summary>
/// Ce premier exemple utilisant l'approche CodeFirst avec EntityFramework montre comment vous pouvez créer simplement
/// une base à partir de vos classes métier, en profitant des conventions de nommage d'Entity Framework.
///
/// Contrairement à EntityFramework 6 dans le .NET framework 4.6, il y a un "petit peu" plus de travail à fournir, mais pas des masses... :)
/// Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,
/// - dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),
/// - dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_001_EF_CodeFirst_conventions)
/// - tapez : dotnet restore (pour restaurer les packages .NET Core)
/// - tapez : dotnet ef migrations add MyFirstMigration
/// note : vous pourrez détruire le dossier Migrations une fois la base créée
/// - tapez : dotnet ef database update
/// Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.
/// Dans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf
///
/// Nous allons nous attarder sur la classe Nounours et NounoursDBEntities.
/// 1/ Nounours -> voir commentaires dans la classe Nounours
/// 2/ NounoursDBEntities -> voir commentaires dans la classe NounoursDBEntities
///
/// Ensuite, nous pouvons donc instancier NounoursDBEntities (à l'aide d'un bloc using car il s'agit d'une ressource non managée).
/// Celle-ci donne accès à la DbSet<Nounours> à laquelle nous rajoutons 3 Nounours.
/// Mais avant cela, nous vérifions qu'ils n'existent pas déjà en base, et si c'est le cas, nous les détruisons.
///
/// Ces modifications sont persistées dans la base lors de l'appel à SaveChanges.
///
///
/// Pour visualiser les résultats, vous pouvez :
/// - aller dans Affichage -> Explorateur d'objets SQL Server
/// - dans cet explorateur, SQL Server -> (localdb)\MSSQLLocalDB... -> Bases de données
/// -> ex_042_001_EF_CodeFirst_conventions -> Tables -> dbo.Nounours
/// en cliquant sur dbo.Nounours vous pourrez voir la table
/// avec un clic droit -> "Afficher les données" sur dbo.Nounours vous pourrez voir les données dans la table
///
/// </summary>
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Nounours trixi = new Nounours { Nom = "Trixi", DateDeNaissance = new DateTime(2015, 8, 15), NbPoils = 0 };
Nounours Roudoudou = new Nounours { Nom = "Roudoudou", DateDeNaissance = new DateTime(2015, 8, 14), NbPoils = 2000 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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}");
}
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok, trixi, Roudoudou });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_001_EF_CodeFirst_conventions)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

@ -1,119 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_002_EF_CodeFirst_data_annotations
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// Nous avons vu dans l'exemple précédent comment utiliser les conventions d'écriture. Cette méthode est pratique mais ne nous laisse pas beaucoup
/// de possibilités de modifier la table selon nos désirs.
/// Cet exemple montre comment utiliser les data annotations. En voici quelques exemples principaux :
/// - la classe utilise l'annotation [Table("TableNounours")] qui permet de modifier le nom de la table dans la base de données
/// - la propriété DateDeNaissance a une annotation "Column" permettant de choisir un nom pour la colonne
/// - la propriété Nom n'a pas d'annotation "Column", c'est donc la convention d'écriture qui s'applique pour le nom de la colonne => "Nom"
/// - la propriété Nom a une annotation "Required" qui indique que le champ est obligatoire
/// - la propriété Nom a une annotation MaxLength indiquant combien de caractères peut avoir le Nom au maximum dans la base de données
/// - la propriété NbPoils a une annotation NotMapped indiquant qu'il n'y aura pas de colonne associée à cette propriété dans la table
/// - la propriété UniqueId a une annotation Key pour indiquer que c'est cette propriété qu'il faut utiliser comme clé primaire
/// - la propriété UniqueId a également une annotation DatabaseGenerated permettant d'indiquer que c'est lors de l'insertion dans la table qu'une
/// clé primaire (ici de type Guid, et pas obligatoirement int) lui sera attribuée
///
/// Il existe d'autres annotations que vous pouvez découvrir et utiliser par la suite comme ForeignKey, TimeStamp, ConcurrencyCheck, CreditCard,
/// EmailAddress, Phone, StringLength, Url...
/// </summary>
[Table("TableNounours")]
public class Nounours
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UniqueId
{
get; set;
}
[Required]
[MaxLength(256)]
public string Nom
{
get;
set;
}
[Column("Naissance")]
public DateTime DateDeNaissance
{
get;
set;
}
[NotMapped]
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,34 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-12
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_002_EF_CodeFirst_data_annotations
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
///
/// Lorsque nous utiliserons cette classe, le programme cherchera la chaîne de connection (connectionString) avec le même nom que celui passé
/// dans le constructeur de DbContext (ici NounoursDBContext). Il cherche la connectionString à différents endroits et en particulier ici
/// dans App.config.
/// Si cette table n'existe pas, il va la créer automatiquement. Ceci est configurable et sera présenté dans les exemples suivants.
/// </summary>
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_002_EF_CodeFirst_data_annotations.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -1,124 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
using static System.Console;
using System.Linq;
namespace ex_042_002_EF_CodeFirst_data_annotations
{
/// <summary>
/// Ce deuxième exemple utilisant l'approche CodeFirst avec EntityFramework montre comment vous pouvez créer simplement
/// une base à partir de vos classes métier, en profitant des data annotations d'Entity Framework.
///
/// Seule la classe Nounours est différente. Tout le reste de l'exemple est le même que le précédent.
///
/// Contrairement à EntityFramework 6 dans le .NET framework 4.6, il y a un "petit peu" plus de travail à fournir, mais pas des masses... :)
/// Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,
/// - dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),
/// - dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_001_EF_CodeFirst_conventions)
/// - tapez : dotnet restore (pour restaurer les packages .NET Core)
/// - tapez : dotnet ef migrations add MyFirstMigration
/// note : vous pourrez détruire le dossier Migrations une fois la base créée
/// - tapez : dotnet ef database update
/// Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.
/// Dans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf
///
///
/// Nous allons nous attarder sur la classe Nounours et NounoursDBEntities.
/// 1/ Nounours -> voir commentaires dans la classe Nounours
/// 2/ NounoursDBEntities -> voir commentaires dans la classe NounoursDBEntities
///
/// Ensuite, nous pouvons donc instancier NounoursDBEntities (à l'aide d'un bloc using car il s'agit d'une ressource non managée).
/// Celle-ci donne accès à la DbSet<Nounours> à laquelle nous rajoutons 3 Nounours.
///
/// Ces modifications sont persistées dans la base lors de l'appel à SaveChanges.
///
/// Pour visualiser les résultats, vous pouvez :
/// - aller dans Affichage -> Explorateur d'objets SQL Server
/// - dans cet explorateur, SQL Server -> (localdb)\MSSQLLocalDB... -> Bases de données
/// -> ex_042_002_EF_CodeFirst_data_annotations -> Tables -> dbo.TableNounours
/// en cliquant sur dbo.TableNounours vous pourrez voir la table
/// avec un clic droit -> "Afficher les données" sur dbo.Nounours vous pourrez voir les données dans la table
/// </summary>
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Nounours trixi = new Nounours { Nom = "Trixi", DateDeNaissance = new DateTime(2015, 8, 15), NbPoils = 0 };
Nounours Roudoudou = new Nounours { Nom = "Roudoudou", DateDeNaissance = new DateTime(2015, 8, 14), NbPoils = 2000 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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}");
}
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok, trixi, Roudoudou });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_002_EF_CodeFirst_data_annotations)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

@ -1,100 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
namespace ex_042_003_EF_CodeFirst_Fluent_API
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// Elle contient 4 propriétés en lecture/écriture : Nom, DateDeNaissance, NbPoils et UniqueId.
/// Entity Framework va utiliser la classe POCO Nounours pour créer une table dans la base de données, lorsque le DbSet va être créé (nous verrons ça dans la classe NounoursDBEntities).
///
/// Dans cet exemple, nous n'utilisons pas les annotations de données (data annotations) et nous n'utiliserons pas non plus implicitement les conventions d'écriture
/// d'Entity Framework car nous allons les réécrire dans la classe NounoursDBEntities.
/// </summary>
public class Nounours
{
public Guid UniqueId
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,119 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-16
//
// ========================================================================
using System;
using static System.Console;
using System.Linq;
namespace ex_042_003_EF_CodeFirst_Fluent_API
{
/// <summary>
/// Ce troisième exemple utilisant l'approche CodeFirst avec EntityFramework montre comment vous pouvez créer simplement
/// une base à partir de vos classes métier, en profitant de l'API Fluent d'Entity Framework.
///
/// Ici, nous n'expliquerons pas comment utiliser la chaîne de connection ; référez-vous aux exemples suivants pour plus d'informations.
/// Elle est définie dans le fichier App.config avec le nom NounoursDBContext.
///
/// Nous allons nous attarder sur la classe Nounours et NounoursDBEntities.
/// 1/ Nounours -> voir commentaires dans la classe Nounours
/// 2/ NounoursDBEntities -> voir commentaires dans la classe NounoursDBEntities
/// C'est cette classe NounoursDBEntities qui explique comment traduire Nounours en Table.
///
/// Ensuite, nous pouvons donc instancier NounoursDBEntities (à l'aide d'un bloc using car il s'agit d'une ressource non managée).
/// Celle-ci donne accès à la DbSet<Nounours> à laquelle nous rajoutons 3 Nounours.
///
/// Ces modifications sont persistées dans la base lors de l'appel à SaveChanges.
///
/// Pour visualiser les résultats, vous pouvez :
/// - aller dans Affichage -> Explorateur d'objets SQL Server
/// - dans cet explorateur, SQL Server -> (localdb)\MSSQLLocalDB... -> Bases de données
/// -> ex_042_003_EF_CodeFirst_Fluent_API -> Tables -> dbo.TableNounours
/// en cliquant sur dbo.TableNounours vous pourrez voir la table
/// avec un clic droit -> "Afficher les données" sur dbo.Nounours vous pourrez voir les données dans la table
///
/// Si vous exécutez une deuxième fois le projet après avoir modifié la classe Nounours, celui-ci ne marchera pas
/// car la table créée n'aura plus les bonnes caractéristiques correspondant à la classe Nounours.
/// Vous pouvez pour cela effacer la base, avec un clic droit sur ex_042_003_EF_CodeFirst_Fluent_API -> Supprimer
/// puis relancer le programme.
/// </summary>
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Nounours trixi = new Nounours { Nom = "Trixi", DateDeNaissance = new DateTime(2015, 8, 15), NbPoils = 0 };
Nounours Roudoudou = new Nounours { Nom = "Roudoudou", DateDeNaissance = new DateTime(2015, 8, 14), NbPoils = 2000 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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}");
}
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok, trixi, Roudoudou });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_003_EF_CodeFirst_Fluent_API)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

@ -1,53 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : DbContextInitializer.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
namespace ex_042_004_EF_CF_InitializationStrategy
{
/// <summary>
/// initialiseur de stratégies...
/// </summary>
public static class DbContextInitializer
{
/// <summary>
/// les différentes stratégies de création de la base
/// </summary>
public enum InitializationStrategies
{
CreateDatabaseIfNotExists,
DropCreateDatabaseIfModelChanges,
DropCreateDatabaseAlways
}
public static void SetInitializer(DbContext context, InitializationStrategies strategy)
{
switch(strategy)
{
//par défaut : crée la base seulement si elle n'existe pas
default:
case InitializationStrategies.CreateDatabaseIfNotExists:
context.Database.EnsureCreated();
break;
//recrée la base même si elle existe déjà
case InitializationStrategies.DropCreateDatabaseAlways:
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
break;
//recrée la base seulement si le modèle change : impossible aujourd'hui en Entity Framework Core...
case InitializationStrategies.DropCreateDatabaseIfModelChanges:
throw new NotImplementedException("Le mode DropCreateDatabaseIfModelChanges ne peut pas encore exister sous Entity Framework Core");
}
}
}
}

@ -1,104 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_004_EF_CF_InitializationStrategy
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// </summary>
[Table("TableNounours")]
public class Nounours
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UniqueId
{
get; set;
}
[Required]
[MaxLength(256)]
public string Nom
{
get;
set;
}
[Column("Naissance")]
public DateTime DateDeNaissance
{
get;
set;
}
[NotMapped]
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,29 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_004_EF_CF_InitializationStrategy
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
/// </summary>
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_004_EF_CF_InitializationStrategy.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -1,108 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using System;
using static System.Console;
using System.Linq;
namespace ex_042_004_EF_CF_InitializationStrategy
{
/// <summary>
/// Cet exemple présente les différentes stratégies d'initialisation de la base de données.
///
/// Dans Entity Framework 6 pour .NET Framework (cf. ex_042_004 des exemples .NET Framework), il existe des stratégies d'initialisation.
/// Il n'y a malheureusement pas d'équivalent pour Entity Framework Core.
///
/// Cet exemple reprend le 042_002 tout en proposant une méthode permettant de modifier les stratégies d'initialisation.
/// Pour cela, cet exemple propose l'écriture d'une classe statique DbContextInitializer, mais toutes les stratégies ne sont néanmoins pas reproductibles.
/// </summary>
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Nounours trixi = new Nounours { Nom = "Trixi", DateDeNaissance = new DateTime(2015, 8, 15), NbPoils = 0 };
Nounours Roudoudou = new Nounours { Nom = "Roudoudou", DateDeNaissance = new DateTime(2015, 8, 14), NbPoils = 2000 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
//choix de la stratégie
DbContextInitializer.SetInitializer(db, DbContextInitializer.InitializationStrategies.CreateDatabaseIfNotExists);
//DbContextInitializer.SetInitializer(db, DbContextInitializer.InitializationStrategies.DropCreateDatabaseAlways);
//DbContextInitializer.SetInitializer(db, DbContextInitializer.InitializationStrategies.DropCreateDatabaseIfModelChanges);
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}");
}
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok, trixi, Roudoudou });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
}
catch(NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_004_EF_CF_InitializationStrategy)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

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

@ -1,104 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_005_EF_CF_Seeding_Data
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// </summary>
[Table("TableNounours")]
public class Nounours
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UniqueId
{
get; set;
}
[Required]
[MaxLength(256)]
public string Nom
{
get;
set;
}
[Column("Naissance")]
public DateTime DateDeNaissance
{
get;
set;
}
[NotMapped]
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,29 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_005_EF_CF_Seeding_Data
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
/// </summary>
public class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_005_EF_CF_Seeding_Data.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -1,108 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-17
//
// ========================================================================
using System;
using static System.Console;
using System.Linq;
namespace ex_042_005_EF_CF_Seeding_Data
{
/// <summary>
/// Cet exemple présente les différentes stratégies d'initialisation de la base de données.
///
/// Dans Entity Framework 6 pour .NET Framework (cf. ex_042_004 des exemples .NET Framework), il existe des stratégies d'initialisation.
/// Il n'y a malheureusement pas d'équivalent pour Entity Framework Core.
///
/// Cet exemple reprend le 042_002 tout en proposant une méthode permettant de modifier les stratégies d'initialisation.
/// Pour cela, cet exemple propose l'écriture d'une classe statique DbContextInitializer, mais toutes les stratégies ne sont néanmoins pas reproductibles.
///
/// De plus, elle montre comment injecter des données stubbées pour permettre de rapidement tester la base.
/// </summary>
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Nounours trixi = new Nounours { Nom = "Trixi", DateDeNaissance = new DateTime(2015, 8, 15), NbPoils = 0 };
Nounours Roudoudou = new Nounours { Nom = "Roudoudou", DateDeNaissance = new DateTime(2015, 8, 14), NbPoils = 2000 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
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}");
}
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok, trixi, Roudoudou });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
}
catch (NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_005_EF_CF_Seeding_Data)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

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

@ -1,108 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Nounours.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-18
//
// ========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_006_EF_CF_One_to_One
{
/// <summary>
/// Nounours est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation 1-1 avec la classe CarnetDeSante via la propriété Carnet.
/// La clé primaire est générée lors de l'insertion en table (et est utilisée dans la classe CarnetDeSante
/// pour faire l'association. cf. CarnetDeSante).
[Table("TableNounours")]
public class Nounours
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UniqueId
{
get; set;
}
public virtual CarnetDeSante Carnet { get; set; }
[Required]
[MaxLength(256)]
public string Nom
{
get;
set;
}
[Column("Naissance")]
public DateTime DateDeNaissance
{
get;
set;
}
[NotMapped]
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,30 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-18
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_006_EF_CF_One_to_One
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient deux DbSet<T> pour permettre de réaliser des opérations CRUD sur les types T, ici Nounours et CarnetDeSante.
/// </summary>
public class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
public virtual DbSet<CarnetDeSante> Carnets { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_EF_CF_One_to_One.Nounours.mdf;Trusted_Connection=True;");
}
}
}

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

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

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

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

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

@ -1,31 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : AlbumDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_008_EF_CF_One_to_Many
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient deux DbSet<T> pour permettre de réaliser des opérations CRUD sur les types T, ici Album et Morceau.
/// </summary>
public class AlbumDBEntities : DbContext
{
public virtual DbSet<Album> Albums { get; set; }
public virtual DbSet<Morceau> Morceaux { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_008_EF_CF_One_to_Many.Albums.mdf;Trusted_Connection=True;");
}
}
}

@ -1,85 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : DbContextInitializer.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
namespace ex_042_008_EF_CF_One_to_Many
{
/// <summary>
/// initialiseur de stratégies...
/// </summary>
public static class DbContextInitializer
{
/// <summary>
/// remplissage de la base avec des données stubbées.
/// </summary>
/// <param name="context">base à remplir</param>
public static void Seed(AlbumDBEntities context)
{
SetInitializer(context, InitializationStrategies.DropCreateDatabaseAlways);
Album kindofblue = new Album { Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
Morceau[] kindofblueMorceaux = { new Morceau { Album = kindofblue, Titre = "So What" },
new Morceau { Album = kindofblue, Titre = "Freddie Freeloader" },
new Morceau { Album = kindofblue, Titre = "Blue in Green" },
new Morceau { Album = kindofblue, Titre = "All Blues" },
new Morceau { Album = kindofblue, Titre = "Flamenco Sketches" } };
foreach (var m in kindofblueMorceaux) kindofblue.Morceaux.Add(m);
Album dialogue = new Album { Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
Morceau[] dialogueMorceaux = { new Morceau { Album = dialogue, Titre = "Catta" },
new Morceau { Album = dialogue, Titre = "Idle While" },
new Morceau { Album = dialogue, Titre = "Les Noirs Marchant" },
new Morceau { Album = dialogue, Titre = "Dialogue" },
new Morceau { Album = dialogue, Titre = "Ghetto Lights" },
new Morceau { Album = dialogue, Titre = "Jasper" } };
foreach (var m in dialogueMorceaux) dialogue.Morceaux.Add(m);
context.Albums.AddRange(new Album[] { kindofblue, dialogue });
context.Morceaux.AddRange(kindofblueMorceaux);
context.Morceaux.AddRange(dialogueMorceaux);
context.SaveChanges();
}
/// <summary>
/// les différentes stratégies de création de la base
/// </summary>
public enum InitializationStrategies
{
CreateDatabaseIfNotExists,
DropCreateDatabaseIfModelChanges,
DropCreateDatabaseAlways
}
public static void SetInitializer(DbContext context, InitializationStrategies strategy)
{
switch (strategy)
{
//par défaut : crée la base seulement si elle n'existe pas
default:
case InitializationStrategies.CreateDatabaseIfNotExists:
context.Database.EnsureCreated();
break;
//recrée la base même si elle existe déjà
case InitializationStrategies.DropCreateDatabaseAlways:
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
break;
//recrée la base seulement si le modèle change : impossible aujourd'hui en Entity Framework Core...
case InitializationStrategies.DropCreateDatabaseIfModelChanges:
throw new NotImplementedException("Le mode DropCreateDatabaseIfModelChanges ne peut pas encore exister sous Entity Framework Core");
}
}
}
}

@ -1,84 +0,0 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Program.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-19
//
// ========================================================================
using System;
using static System.Console;
namespace ex_042_008_EF_CF_One_to_Many
{
class Program
{
/// <summary>
/// Cet exemple montre comment construire une relation 1-many dans la base de données en utilisant les conventions de nommage Entity Framework.
///
/// on utilise les données stubbées de DbContextInitializer
/// On affiche les Albums et les Morceaux.
/// Constatez que les identifiants sont bien les mêmes à cause de la relation 1-many.
///
/// Si vous ouvrez la base de données (via l'explorateur d'objets SQL Server), vous pourrez constater que la table Morceaux
/// contient une colonne Album_UniqueId qui permet d'assurer la relation 1-many.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
try
{
//création du DbContext et injection de la dépendance à MyStubDataInitializationStrategy
using (AlbumDBEntities db = new AlbumDBEntities())
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
WriteLine("Albums : ");
foreach (var a in db.Albums)
{
WriteLine($"\t{a.UniqueId}: {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.UniqueId}: {m.Titre} (album : {m.Album.Titre})");
}
}
}
catch (NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (Exception)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_008_EF_CF_One_to_Many)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

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

@ -51,12 +51,12 @@ namespace ex_042_009_EF_CF_One_to_Many_FluentAPI
//on crée une propriété fantôme (shadow property) pour l'entity Morceau qu'on utilisera comme clé étrangère (foreign key)
// En effet, j'ai pris le cas où nous décidons d'utiliser une classe existante pour laquelle aucune clé étrangère n'a été prévue.
// Il me semblait donc logique qu'on cherche à l'utiliser sans qu'elle soit dans le modèle
modelBuilder.Entity<Morceau>().Property<Guid>("MorceauId");
modelBuilder.Entity<Morceau>().Property<Guid>("AlbumId");
//on précise qu'il y a une relation entre Album et Morceau
modelBuilder.Entity<Album>().HasMany(a => a.Morceaux) // on dit que l'Album a plusieurs morceaux
.WithOne(m => m.Album) // et que chaque morceau a un album
.HasForeignKey("MorceauId"); //on utilise la clé étrangère fantôme créée précédemment
.HasForeignKey("AlbumId"); //on utilise la clé étrangère fantôme créée précédemment
//Si vous regardez la table obtenue, vous pouvez voir qu'Entity Framework a automatiquement généré une clé étrangère.

@ -39,7 +39,8 @@ namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
WriteLine("Albums : ");
foreach (var album in db.Albums.Include(a => a.AlbumsArtistes).ThenInclude(aa => aa.Artiste))
foreach (var album in db.Albums.Include(a => a.AlbumsArtistes)
.ThenInclude(aa => aa.Artiste))
{
WriteLine($"\t{album.UniqueId}: {album.Titre} (sorti le : {album.DateDeSortie.ToString("d")})");
foreach (var artiste in album.Artistes)
@ -51,7 +52,8 @@ namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
WriteLine();
WriteLine("Artistes :");
foreach (var artiste in db.Artistes.Include(a => a.AlbumsArtistes).ThenInclude(aa => aa.Album))
foreach (var artiste in db.Artistes.Include(a => a.AlbumsArtistes)
.ThenInclude(aa => aa.Album))
{
var annéeDeMort = artiste.DateDeMort.HasValue ? $" - {artiste.DateDeMort.Value.Year}" : "";
var titresAlbums = artiste.Albums.Aggregate(String.Empty, (albums, album) => albums + $"\"{album.Titre}\" ");

@ -0,0 +1,282 @@
# Entity Framework Core 3.0
*25/01/2020 &sdot; 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.
---
*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* :
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.
* [**ex_041_001 : Connection Strings**](ex_041_001_ConnectionStrings) : montre comment utiliser une chaîne de connexion SQL Server ou SQLite.
* [**ex_041_004 : Testing in memory**](ex_041_004_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.
* [**ex_042_001 : conventions d'écriture**](ex_042_001_EF_CF_conventions) : explique quelles sont les conventions d'écriture utilisées pour la transformation d'une entité en table.
* [**ex_042_002 : data annotations**](ex_042_002_EF_CF_data_annotations) : explique comment utiliser les *data annotations* pour personnaliser la transformation d'une entité en table.
* [**ex_042_003 : Fluent API**](ex_042_003_EF_CF_Fluent_API) : explique comment utiliser la *Fluent API* pour personnaliser la transformation d'une entité en table.
* [**ex_042_004 : Keys with conventions**](ex_042_004_Keys_conventions) : explique comment créer les clés primaires d'une entité lorsqu'on utilise les conventions d'écriture.
* [**ex_042_005 : Keys with data annotations**](ex_042_005_Keys_data_annotations) : explique comment créer les clés primaires d'une entité lorsqu'on utilise les *data annotations*.
* [**ex_042_006 : Keys with Fluent API**](ex_042_006_Keys_FluentAPI) : explique comment créer les clés primaires d'une entité lorsqu'on utilise la *Fluent API*.
* [**ex_042_007 : Value Generation**](ex_042_007_ValueGeneration) : explique comment faire générer des valeurs automatiquement lors de l'insertion ou de la mise à jour
* [**ex_042_008 : Data Seeding before Entity Framework 2.1**](ex_042_008_DataSeeding_before_EF2.1) : explique comment utiliser un stub (méthode qui était recommandée avant EF Core 2.1)
* [**ex_042_009 : Data Seeding**](ex_042_009_DataSeeding) : explique comment utiliser un stub (méthode recommandée depuis EF Core 2.1)
* [**Relationships**](Relationships.md) : en cliquant [ici](Relationships.md), vous aurez plus de détails sur les relations entre entités
* [**ex_042_010 : Single Property navigation with data annotations**](ex_042_010_SinglePropertyNavigation_conventions) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant les conventions d'écriture et/ou les annotations de données.
* [**ex_042_011 : Single Property navigation with Fluent API**](ex_042_011_SinglePropertyNavigation_FluentAPI) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant la *FLuent API*.
* [**ex_042_012 : One To One with data annotations**](ex_042_012_OneToOne_conventions) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant l'*annotation de données*.
* [**ex_042_013 : One To One with Fluent API**](ex_042_013_OneToOne_FluentAPI) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant la *FluentAPI*.
* [**ex_042_014 : One To Many with data annotations**](ex_042_014_OneToMany_dataAnnotations) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant l'*annotation de données*.
* [**ex_042_015 : One To Many with conventions**](ex_042_015_OneToMany_conventions) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant les *conventions d'écriture*.
* [**ex_042_016 : One To Many with Fluent API**](ex_042_016_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
*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
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
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
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
// 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
using System.Linq;
```
* Editez *Program.cs* pour rajouter les lignes suivantes à la fin de la méthode ```Main``` :
```csharp
// 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
// 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
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.

@ -0,0 +1,30 @@
# 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)

@ -0,0 +1,10 @@
using System;
namespace ex_041_001_ConnectionStrings
{
class Nounours
{
public int Id { get; set; }
public string Nom { get; set; }
}
}

@ -0,0 +1,56 @@
using System;
using System.Runtime.InteropServices;
namespace ex_041_001_ConnectionStrings
{
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" };
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
using (var context = new SqlServerContext())
{
// Crée des nounours et les insère dans la base
Console.WriteLine("Creates and inserts new Nounours with SqlServer");
context.Add(chewie);
context.Add(yoda);
context.Add(ewok);
context.SaveChanges();
}
using (var context = new SqlServerContext())
{
foreach(var n in context.Nounours)
{
Console.WriteLine($"{n.Id} - {n.Nom}");
}
context.SaveChanges();
}
}
using (var context = new SQLiteContext())
{
// Crée des nounours et les insère dans la base
Console.WriteLine("Creates and inserts new Nounours with SQLite");
context.Add(chewie);
context.Add(yoda);
context.Add(ewok);
context.SaveChanges();
}
using (var context = new SQLiteContext())
{
foreach(var n in context.Nounours)
{
Console.WriteLine($"{n.Id} - {n.Nom}");
}
context.SaveChanges();
}
}
}
}

@ -0,0 +1,147 @@
# ex_041_001_ConnectionStrings
*31/12/2019 &sdot; Marc Chevaldonné*
*Dernière mise à jour : 09/01/2020 &sdot; Marc Chevaldonné*
---
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
```
*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.
### Migrations
*Note :* 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
```
*Note : 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
```
*Note : 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*.
<img src="./readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="./readmefiles/sqlserver_02.png" width="460"/>
* 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*.
![DB Browser for SQLite](./readmefiles/dbbrowser_01.png)
* Choisissez l'onglet *Parcourir les données*
* Observez les résultats obtenus
![DB Browser for SQLite](./readmefiles/dbbrowser_02.png)
---
Copyright &copy; 2019-2020 Marc Chevaldonné

@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace ex_041_001_ConnectionStrings
{
class SQLiteContext : DbContext
{
public DbSet<Nounours> Nounours { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source=ex_041_001_ConnectionStrings.Nounours.db");
}
}

@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;
namespace ex_041_001_ConnectionStrings
{
class SqlServerContext : DbContext
{
public DbSet<Nounours> Nounours { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_001_ConnectionStrings.Nounours.mdf;Trusted_Connection=True;");
}
}

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@ -0,0 +1,46 @@
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();
}
using (var context = new SQLiteNounoursContext())
{
foreach(var n in context.Nounours)
{
Console.WriteLine($"{n.Id} - {n.Nom}");
}
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");
}
}
}
}

@ -1,21 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>ex_042_006_EF_CF_One_to_One</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>ex_042_006_EF_CF_One_to_One</PackageId>
<RuntimeFrameworkVersion>3.0.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<TargetFramework>netcoreapp3.0</TargetFramework>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ex_041_004_TestingInMemory\ex_041_004_TestingInMemory.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,34 @@
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();
}
}
}
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ex_041_004_TestingInMemory\ex_041_004_TestingInMemory.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,10 @@
using System;
namespace ex_041_004_TestingInMemory
{
public class Nounours
{
public int Id { get; set; }
public string Nom { get; set; }
}
}

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
namespace ex_041_004_TestingInMemory
{
public class NounoursContext : DbContext
{
public DbSet<Nounours> Nounours { get; set; }
public NounoursContext()
{ }
public NounoursContext(DbContextOptions<NounoursContext> options)
: base(options)
{ }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_TestingInMemory.Nounours.mdf;Trusted_Connection=True;");
}
}
}
}

@ -0,0 +1,557 @@
# ex_041_004_TestingInMemory
*02/01/2020 &sdot; Marc Chevaldonné*
*Dernière mise à jour : 09/01/2020 &sdot; Marc Chevaldonné*
---
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.
**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 :
```csharp
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
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
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
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
```
*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 : 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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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
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
```
*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 : 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.
```
dotnet ef migrations add migration_ex_041_004 --project ../ex_041_004_TestingInMemory
```
* Création de la table :
```
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_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*.
![DB Browser for SQLite](./readmefiles/dbbrowser_01.png)
* Choisissez l'onglet *Parcourir les données*
* Observez les résultats obtenus
![DB Browser for SQLite](./readmefiles/dbbrowser_02.png)
* 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
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
[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
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*
<img src="./readmefiles/readme_01.png" width="460"/>
* Cliquez sur "Exécuter tous les tests"
<img src="./readmefiles/readme_02.png" width="460"/>
* Observez le bon fonctionnement
---
Copyright &copy; 2019-2020 Marc Chevaldonné

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

@ -0,0 +1,80 @@
using ex_041_004_TestingInMemory;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Xunit;
namespace ex_041_004_UnitTests_w_InMemory
{
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);
}
}
[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());
}
}
}
}

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ex_041_004_TestingInMemory\ex_041_004_TestingInMemory.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,102 @@
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());
}
}
}
}

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ex_041_004_TestingInMemory\ex_041_004_TestingInMemory.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,36 @@
using System;
namespace ex_042_001_EF_CF_conventions
{
public class Nounours
{
public int ID
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
public override string ToString()
{
return $"{ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_001_EF_CF_conventions
{
class NounoursDBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_001_EF_CF_conventions.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,70 @@
using Microsoft.Data.SqlClient;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_001_EF_CF_conventions
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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)
{
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}");
}
}
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}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,223 @@
# ex_042_001_EF_CF_conventions
*02/01/2020 &sdot; Marc Chevaldonné*
---
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 :
* **ex_042_002_EF_CF_data_annotations** : avec les *data annotations*
* **ex_042_003_EF_CF_Fluent_API** : avec la *Fluent API*
---
## 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
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
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
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
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
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
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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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.*

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,46 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_002_EF_CF_data_annotations
{
[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)";
}
}
}

@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_002_EF_CF_data_annotations
{
class NounoursDBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_002_EF_CF_data_annotations.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,70 @@
using Microsoft.Data.SqlClient;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_002_EF_CF_data_annotations
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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)
{
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}");
}
}
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}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,309 @@
# ex_042_002_EF_CF_data_annotations
*03/01/2020 &sdot; Marc Chevaldonné*
---
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 :
* **ex_042_001_EF_CF_conventions** : avec les *conventions d'écriture*
* **ex_042_003_EF_CF_Fluent_API** : avec la *Fluent API*
---
## 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
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
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
[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
[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
[Column("Naissance")]
public DateTime DateDeNaissance
{
get;
set;
}
```
* On pourrait aller plus loin en indiquant l'ordre et le changement de type, par exemple :
```csharp
[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
[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
[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
[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
[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
[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
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
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
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_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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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
*Notes: les identifiants seront bien sûr différents.*
*Notez l'absence de la colonne "NbPoils"*
*Notez le nom de la colonne "Naissance" et le formatage de la date*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,36 @@
using System;
namespace ex_042_003_EF_CF_Fluent_API
{
public class Nounours
{
public Guid UniqueId
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -1,86 +1,67 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : NounoursDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-12
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
namespace ex_042_003_EF_CodeFirst_Fluent_API
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
///
/// Lorsque nous utiliserons cette classe, le programme cherchera la chaîne de connection (connectionString) avec le même nom que celui passé
/// dans le constructeur de DbContext (ici NounoursDBContext). Il cherche la connectionString à différents endroits et en particulier ici
/// dans App.config.
/// Si cette table n'existe pas, il va la créer automatiquement. Ceci est configurable et sera présenté dans les exemples suivants.
///
/// Contrairement aux 2 exemples précédents, nous n'utilisons pas les conventions d'écriture, ni les annotations de données pour utiliser créer la table
/// à partir de la classe Nounours. Nous allons cette fois-ci utiliser la Fluent API et réécrire dans la classe NounoursDBEntities dérivant de DbContext
/// de quelle manière créer la table à partir de la classe Nounours.
///
/// 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.
///
/// Pour utiliser la Fluent API et décrire de quelle manière créer la table à partir de la classe POCO, il faut réécrire la méthode virtuelle OnModelCreating
/// (cf. commentaires de la méthode ci-dessous).
///
/// L'exemple ci-dessous recrée exactement la même table que dans l'exemple précédent.
/// </summary>
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_003_EF_CodeFirst_Fluent_API.Nounours.mdf;Trusted_Connection=True;");
}
/// <summary>
/// méthode appelée lors de la création du modèle.
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//ici on précise comment s'appellera la table associée à la classe POCO Nounours
//équivalent du [Table("TableNounours")] avec les annotations de données
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
//ici on précise que la propriété UniqueId de Nounours est la clef primaire
//équivalent de [Key] devant la propriété UniqueId dans Nounours
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
//ici on explique que c'est lors de l'insertion en base que la clef primaire sera générée
//équivalent de [DatabaseGenerated(DatabaseGeneratedOption.Identity)] devant la propriété UniqueId de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
//HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
//ici on précise que la propriété Nom est obligatoire et que sa taille maximale est de 256 caractères
//Notez l'utilisation des méthodes chaînées ! Trop beau ! Tellement pratique, intuitif et évident...
//équivalent de [Required] et [MaxLength(256)] devant la propriété Nom de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
.HasMaxLength(256);
//ici on donne un nom à la colonne associée à la propriété DateDeNaissance
//équivalent de [Column("Naissance")] devant la propriété DateDeNaissance de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance");
//ici on précise que la propriété NbPoils ne sera pas insérée en base
//équivalent de [NotMapped] devant la propriété NbPoils de Nounours
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
base.OnModelCreating(modelBuilder);
}
}
}
using Microsoft.EntityFrameworkCore;
namespace ex_042_003_EF_CF_Fluent_API
{
/// <summary>
/// Contrairement aux 2 exemples précédents, nous n'utilisons pas les conventions d'écriture, ni les annotations de données pour utiliser créer la table
/// à partir de la classe Nounours. Nous allons cette fois-ci utiliser la Fluent API et réécrire dans la classe NounoursDBEntities dérivant de DbContext
/// de quelle manière créer la table à partir de la classe Nounours.
///
/// 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.
///
/// Pour utiliser la Fluent API et décrire de quelle manière créer la table à partir de la classe POCO, il faut réécrire la méthode virtuelle OnModelCreating
/// (cf. commentaires de la méthode ci-dessous).
///
/// L'exemple ci-dessous recrée exactement la même table que dans l'exemple précédent.
/// </summary>
class NounoursDBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_003_EF_CF_Fluent_API.Nounours.mdf;Trusted_Connection=True;");
}
/// <summary>
/// méthode appelée lors de la création du modèle.
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//ici on précise comment s'appellera la table associée à la classe POCO Nounours
//équivalent du [Table("TableNounours")] avec les annotations de données
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
//ici on précise que la propriété UniqueId de Nounours est la clef primaire
//équivalent de [Key] devant la propriété UniqueId dans Nounours
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
//ici on explique que c'est lors de l'insertion en base que la clef primaire sera générée
//équivalent de [DatabaseGenerated(DatabaseGeneratedOption.Identity)] devant la propriété UniqueId de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
//HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
//ici on précise que la propriété Nom est obligatoire et que sa taille maximale est de 256 caractères
//Notez l'utilisation des méthodes chaînées ! Trop beau ! Tellement pratique, intuitif et évident...
//équivalent de [Required] et [MaxLength(256)] devant la propriété Nom de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
.HasMaxLength(256);
//ici on donne un nom à la colonne associée à la propriété DateDeNaissance
//équivalent de [Column("Naissance")] devant la propriété DateDeNaissance de Nounours
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date");
//ici on précise que la propriété NbPoils ne sera pas insérée en base
//équivalent de [NotMapped] devant la propriété NbPoils de Nounours
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
base.OnModelCreating(modelBuilder);
}
}
}

@ -0,0 +1,70 @@
using Microsoft.Data.SqlClient;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_003_EF_CF_Fluent_API
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
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)
{
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}");
}
}
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}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,308 @@
# ex_042_003_EF_CF_Fluent_API
*03/01/2020 &sdot; Marc Chevaldonné*
---
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 :
* **ex_042_001_EF_CF_conventions** : avec les *conventions d'écriture*
* **ex_042_002_EF_CF_data_annotations** : avec les *data annotations*
---
## 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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_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)
```
*Notes: 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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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
*Notes: les identifiants seront bien sûr différents.*
*Notez l'absence de la colonne "NbPoils"*
*Notez le nom de la colonne "Naissance" et le formatage de la date*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ex_042_004_Keys_conventions
{
public class Cylon
{
public int CylonId
{
get; set;
}
public string Name
{
get; set;
}
public int Generation
{
get; set;
}
public override string ToString()
{
return $"Cylon {CylonId}: {Name}, Number {Generation}";
}
}
}

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_004_Keys_conventions
{
class DBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
public DbSet<Cylon> CylonsSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_004_Keys_conventions.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,36 @@
using System;
namespace ex_042_004_Keys_conventions
{
public class Nounours
{
public int ID
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
public override string ToString()
{
return $"Nounours {ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,79 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_004_Keys_conventions
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 { Name = "John Cavil", Generation = 1 };
Cylon c2 = new Cylon { Name = "Leoben Conoy", Generation = 2 };
Cylon c3 = new Cylon { Name = "D'Anna Biers", Generation = 3 };
Cylon c4 = new Cylon { Name = "Simon", Generation = 4 };
Cylon c5 = new Cylon { Name = "Aaron Doral", Generation = 5 };
Cylon c6 = new Cylon { Name = "Caprica 6", Generation = 6 };
Cylon c7 = new Cylon { Name = "Daniel", Generation = 7 };
Cylon c8 = new Cylon { Name = "Boomer", Generation = 8 };
Cylon c9 = new Cylon { Name = "Athena", Generation = 8 };
try
{
using (DBEntities db = new DBEntities())
{
//nettoyage de la base de données
if (db.NounoursSet.Count() > 0)
{
foreach (var n in db.NounoursSet)
{
db.NounoursSet.Remove(n);
}
db.SaveChanges();
}
if (db.CylonsSet.Count() > 0)
{
foreach (var c in db.CylonsSet)
{
db.CylonsSet.Remove(c);
}
db.SaveChanges();
}
//ajout des nounours dans la base de données
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.CylonsSet.AddRange(new Cylon[] { c1, c2, c3, c4, c5, c6, c7, c8, c9 });
db.SaveChanges();
}
using (DBEntities db = new DBEntities())
{
WriteLine("database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
foreach (var c in db.CylonsSet)
{
WriteLine($"\t{c}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,116 @@
# ex_042_004_Keys_conventions
*06/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple traite des clés primaires associées aux entités.
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 : *ex_041_001_ConnectionStrings*
* les liens entre entités et tables : *ex_042_001_EF_CF_conventions*, *ex_042_002_EF_CF_data_annotations* et *ex_042_003_EF_CF_Fluent_API*
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation des **conventions d'écriture**.
Vous pourrez trouver une version plus ou moins équivalente avec les *data annotations* ici : **ex_042_005_Keys_data_annotations**.
Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : **ex_042_006_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
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
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```.
*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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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.*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
namespace ex_042_005_Keys_data_annotations
{
public class Cylon
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int FrakId
{
get; set;
}
public string Name
{
get; set;
}
public int Generation
{
get; set;
}
public override string ToString()
{
return $"Cylon {FrakId}: {Name}, Number {Generation}";
}
}
}

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_005_Keys_data_annotations
{
class DBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
public DbSet<Cylon> CylonsSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_005_Keys_data_annotations.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,40 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_005_Keys_data_annotations
{
public class Nounours
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UniqueId
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
public override string ToString()
{
return $"Nounours {UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,79 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_005_Keys_data_annotations
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
try
{
using (DBEntities db = new DBEntities())
{
//nettoyage de la base de données
if (db.NounoursSet.Count() > 0)
{
foreach (var n in db.NounoursSet)
{
db.NounoursSet.Remove(n);
}
db.SaveChanges();
}
if (db.CylonsSet.Count() > 0)
{
foreach (var c in db.CylonsSet)
{
db.CylonsSet.Remove(c);
}
db.SaveChanges();
}
//ajout des nounours dans la base de données
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.CylonsSet.AddRange(new Cylon[] { c1, c2, c3, c4, c5, c6, c7, c8, c9 });
db.SaveChanges();
}
using (DBEntities db = new DBEntities())
{
WriteLine("database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
foreach (var c in db.CylonsSet)
{
WriteLine($"\t{c}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,131 @@
# ex_042_005_Keys_data_annotations_
*06/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple traite des clés primaires associées aux entités.
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 : *ex_041_001_ConnectionStrings*
* les liens entre entités et tables : *ex_042_001_EF_CF_conventions*, *ex_042_002_EF_CF_data_annotations* et *ex_042_003_EF_CF_Fluent_API*
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation des **data annotations**.
Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : **ex_042_004_Keys_conventions**.
Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : **ex_042_006_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
[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
[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
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```.
*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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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.*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
namespace ex_042_006_Keys_FluentAPI
{
public class Cylon
{
public int FrakId
{
get; set;
}
public string Name
{
get; set;
}
public int Generation
{
get; set;
}
public override string ToString()
{
return $"Cylon {FrakId}: {Name}, Number {Generation}";
}
}
}

@ -0,0 +1,36 @@
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);
}
}
}

@ -0,0 +1,38 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_006_Keys_FluentAPI
{
public class Nounours
{
public int UniqueId
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
public override string ToString()
{
return $"Nounours {UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ex_042_006_Keys_FluentAPI
{
class Ordinateur
{
public string Modele
{
get; set;
}
public string CodeId
{
get; set;
}
public int Année
{
get; set;
}
public override string ToString()
{
return $"Computer {CodeId} ({Modele}, {Année})";
}
}
}

@ -0,0 +1,100 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_006_Keys_FluentAPI
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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" };
try
{
using (DBEntities db = new DBEntities())
{
//nettoyage de la base de données
if (db.NounoursSet.Count() > 0)
{
foreach (var n in db.NounoursSet)
{
db.NounoursSet.Remove(n);
}
db.SaveChanges();
}
if (db.CylonsSet.Count() > 0)
{
foreach (var c in db.CylonsSet)
{
db.CylonsSet.Remove(c);
}
db.SaveChanges();
}
if (db.Ordinateurs.Count() > 0)
{
foreach (var o in db.Ordinateurs)
{
db.Ordinateurs.Remove(o);
}
db.SaveChanges();
}
//ajout des nounours dans la base de données
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.CylonsSet.AddRange(new Cylon[] { c1, c2, c3, c4, c5, c6, c7, c8, c9 });
db.Ordinateurs.AddRange(new Ordinateur[] { o1, o2, o3, o4, o5, o6 });
db.SaveChanges();
}
using (DBEntities db = new DBEntities())
{
WriteLine("database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
foreach (var c in db.CylonsSet)
{
WriteLine($"\t{c}");
}
foreach (var o in db.Ordinateurs)
{
WriteLine($"\t{o}");
}
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,195 @@
# ex_042_006_Keys_FluentAPI
*07/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple traite des clés primaires associées aux entités.
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 : *ex_041_001_ConnectionStrings*
* les liens entre entités et tables : *ex_042_001_EF_CF_conventions*, *ex_042_002_EF_CF_data_annotations* et *ex_042_003_EF_CF_Fluent_API*
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation la **Fluent API**.
Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : **ex_042_004_Keys_conventions**.
Vous pourrez trouver une version plus ou moins équivalente avec la *data annotations* ici : **ex_042_005_Keys_data_annotations**.
---
## 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
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
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```.
*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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* 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*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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.*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,54 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_007_ValueGeneration
{
[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;
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on: {DateDInsertion}, Last modified on: {DernièreModification})";
}
}
}

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
namespace ex_042_007_ValueGeneration
{
class NounoursDBEntities : DbContext
{
public DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_007_ValueGeneration.Nounours.mdf;Trusted_Connection=True;");
}
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);
}
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();
}
}
}

@ -0,0 +1,79 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_007_ValueGeneration
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 ();
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
//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}");
}
}
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();
}
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}") ;
}
}
catch (SqlException)
{
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save