├── .gitignore ├── Finished sample ├── DishesAPI.sln └── DishesAPI │ ├── DbContexts │ └── DishesDbContext.cs │ ├── Dishes.db │ ├── Dishes.db-shm │ ├── Dishes.db-wal │ ├── DishesAPI.csproj │ ├── EndpointFilters │ ├── DishIsLockedFilter.cs │ ├── LogNotFoundResponseFilter.cs │ └── ValidateAnnotationsFilter.cs │ ├── EndpointHandlers │ ├── DishesHandlers.cs │ └── IngredientsHandlers.cs │ ├── Entities │ ├── Dish.cs │ └── Ingredient.cs │ ├── Extensions │ └── EndpointRouteBuilderExtensions.cs │ ├── Migrations │ ├── 20230314101754_InitialMigration.Designer.cs │ ├── 20230314101754_InitialMigration.cs │ └── DishesDbContextModelSnapshot.cs │ ├── Models │ ├── DishDto.cs │ ├── DishForCreationDto.cs │ ├── DishForUpdateDto.cs │ └── IngredientDto.cs │ ├── Profiles │ ├── DishProfile.cs │ └── IngredientProfile.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json ├── LICENSE ├── README.md └── Starter files ├── ASP.NET Core Minimal API.postman_collection.json ├── DbContexts └── DishesDbContext.cs └── Entities ├── Dish.cs └── Ingredient.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33414.496 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DishesAPI", "DishesAPI\DishesAPI.csproj", "{2A6BAD83-AD6B-4CE7-BC6A-EB37C37CEEDD}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2A6BAD83-AD6B-4CE7-BC6A-EB37C37CEEDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2A6BAD83-AD6B-4CE7-BC6A-EB37C37CEEDD}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2A6BAD83-AD6B-4CE7-BC6A-EB37C37CEEDD}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2A6BAD83-AD6B-4CE7-BC6A-EB37C37CEEDD}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6C678F3D-05DC-43E7-8058-269BD6721794} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/DbContexts/DishesDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.Hosting; 3 | using DishesAPI.Entities; 4 | using System.IO; 5 | 6 | namespace DishesAPI.DbContexts; 7 | 8 | public class DishesDbContext : DbContext 9 | { 10 | public DbSet Dishes { get; set; } = null!; 11 | public DbSet Ingredients { get; set; } = null!; 12 | 13 | 14 | public DishesDbContext(DbContextOptions options) 15 | : base(options) 16 | { 17 | } 18 | 19 | protected override void OnModelCreating(ModelBuilder modelBuilder) 20 | { 21 | _ = modelBuilder.Entity().HasData( 22 | new(Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35"), "Beef"), 23 | new(Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96"), "Onion"), 24 | new(Guid.Parse("c19099ed-94db-44ba-885b-0ad7205d5e40"), "Dark beer"), 25 | new(Guid.Parse("0c4dc798-b38b-4a1c-905c-a9e76dbef17b"), "Brown piece of bread"), 26 | new(Guid.Parse("937b1ba1-7969-4324-9ab5-afb0e4d875e6"), "Mustard"), 27 | new(Guid.Parse("7a2fbc72-bb33-49de-bd23-c78fceb367fc"), "Chicory"), 28 | new(Guid.Parse("b5f336e2-c226-4389-aac3-2499325a3de9"), "Mayo"), 29 | new(Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe"), "Various spices"), 30 | new(Guid.Parse("aab31c70-57ce-4b6d-a66c-9c1b094e915d"), "Mussels"), 31 | new(Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f"), "Celery"), 32 | new(Guid.Parse("8d5a1b40-6677-4545-b6e8-5ba93efda0a1"), "French fries"), 33 | new(Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4"), "Tomato"), 34 | new(Guid.Parse("f350e1a0-38de-42fe-ada5-ae436378ee5b"), "Tomato paste"), 35 | new(Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d"), "Bay leave"), 36 | new(Guid.Parse("b617df23-3d91-40e1-99aa-b07d264aa937"), "Carrot"), 37 | new(Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56"), "Garlic"), 38 | new(Guid.Parse("ecd396c3-4403-4fbf-83ca-94a8e9d859b3"), "Red wine"), 39 | new(Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671"), "Coconut milk"), 40 | new(Guid.Parse("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7"), "Ginger"), 41 | new(Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237"), "Chili pepper"), 42 | new(Guid.Parse("e0017fe1-773f-4a59-9730-9489833c6e8e"), "Tamarind paste"), 43 | new(Guid.Parse("c9b46f9c-d6ce-42c3-8736-2cddbbadee10"), "Firm fish"), 44 | new(Guid.Parse("a07cde83-3127-45da-bbd5-04a7c8d13aa4"), "Ginger garlic paste"), 45 | new(Guid.Parse("ebe94d5d-2ad8-4886-b246-05a1fad83d1c"), "Garam masala")); 46 | 47 | _ = modelBuilder.Entity().HasData( 48 | new(Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 49 | "Flemish Beef stew with chicory" ), 50 | new(Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 51 | "Mussels with french fries" ), 52 | new(Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 53 | "Ragu alla bolognaise"), 54 | new(Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), 55 | "Rendang"), 56 | new(Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), 57 | "Fish Masala")); 58 | 59 | _ = modelBuilder 60 | .Entity() 61 | .HasMany(d => d.Ingredients) 62 | .WithMany(i => i.Dishes) 63 | .UsingEntity(e => e.HasData( 64 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 65 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 66 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("c19099ed-94db-44ba-885b-0ad7205d5e40") }, 67 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("0c4dc798-b38b-4a1c-905c-a9e76dbef17b") }, 68 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("937b1ba1-7969-4324-9ab5-afb0e4d875e6") }, 69 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("7a2fbc72-bb33-49de-bd23-c78fceb367fc") }, 70 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("b5f336e2-c226-4389-aac3-2499325a3de9") }, 71 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 72 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 73 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("aab31c70-57ce-4b6d-a66c-9c1b094e915d") }, 74 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") }, 75 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("8d5a1b40-6677-4545-b6e8-5ba93efda0a1") }, 76 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 77 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 78 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 79 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4") }, 80 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("f350e1a0-38de-42fe-ada5-ae436378ee5b") }, 81 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 82 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") }, 83 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("b617df23-3d91-40e1-99aa-b07d264aa937") }, 84 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 85 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 86 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("ecd396c3-4403-4fbf-83ca-94a8e9d859b3") }, 87 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 88 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 89 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671") }, 90 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 91 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7") }, 92 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237") }, 93 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 94 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("e0017fe1-773f-4a59-9730-9489833c6e8e") }, 95 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 96 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c9b46f9c-d6ce-42c3-8736-2cddbbadee10") }, 97 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("a07cde83-3127-45da-bbd5-04a7c8d13aa4") }, 98 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("ebe94d5d-2ad8-4886-b246-05a1fad83d1c") }, 99 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 100 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4") }, 101 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671") }, 102 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 103 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237") }, 104 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") } 105 | )); 106 | 107 | base.OnModelCreating(modelBuilder); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Dishes.db: -------------------------------------------------------------------------------- 1 | SQLite format 3@ .c  -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Dishes.db-shm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinDockx/BuildingAspNetCore7MinimalAPIs/f78c1dd9e7b0ce8aba77f755732417a8efb63641/Finished sample/DishesAPI/Dishes.db-shm -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Dishes.db-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinDockx/BuildingAspNetCore7MinimalAPIs/f78c1dd9e7b0ce8aba77f755732417a8efb63641/Finished sample/DishesAPI/Dishes.db-wal -------------------------------------------------------------------------------- /Finished sample/DishesAPI/DishesAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 4fa735d3-b2b1-4593-8e5d-176bfe00abbc 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/EndpointFilters/DishIsLockedFilter.cs: -------------------------------------------------------------------------------- 1 | namespace DishesAPI.EndpointFilters; 2 | 3 | public class DishIsLockedFilter : IEndpointFilter 4 | { 5 | private readonly Guid _lockedDishId; 6 | 7 | public DishIsLockedFilter(Guid lockedDishId) 8 | { 9 | _lockedDishId = lockedDishId; 10 | } 11 | 12 | public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 13 | { 14 | Guid dishId; 15 | if (context.HttpContext.Request.Method == "PUT") 16 | { 17 | dishId = context.GetArgument(2); 18 | } 19 | else if (context.HttpContext.Request.Method == "DELETE") 20 | { 21 | dishId = context.GetArgument(1); 22 | } 23 | else 24 | { 25 | throw new NotSupportedException("This filter is not supported for this scenario."); 26 | } 27 | 28 | if (dishId == _lockedDishId) 29 | { 30 | return TypedResults.Problem(new() 31 | { 32 | Status = 400, 33 | Title = "Dish is perfect and cannot be changed.", 34 | Detail = "You cannot update or delete perfection." 35 | }); 36 | } 37 | 38 | // invoke the next filter 39 | var result = await next.Invoke(context); 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/EndpointFilters/LogNotFoundResponseFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace DishesAPI.EndpointFilters; 4 | 5 | public class LogNotFoundResponseFilter : IEndpointFilter 6 | { 7 | private readonly ILogger _logger; 8 | 9 | public LogNotFoundResponseFilter(ILogger logger) 10 | { 11 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 12 | } 13 | 14 | public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 15 | { 16 | var result = await next(context); 17 | var actualResult = (result is INestedHttpResult) ? ((INestedHttpResult)result).Result : (IResult)result; 18 | 19 | if ((actualResult as IStatusCodeHttpResult)?.StatusCode == (int)HttpStatusCode.NotFound) 20 | { 21 | _logger.LogInformation($"Resource {context.HttpContext.Request.Path} was not found."); 22 | } 23 | 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/EndpointFilters/ValidateAnnotationsFilter.cs: -------------------------------------------------------------------------------- 1 | using DishesAPI.Models; 2 | using MiniValidation; 3 | 4 | namespace DishesAPI.EndpointFilters; 5 | 6 | public class ValidateAnnotationsFilter : IEndpointFilter 7 | { 8 | public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 9 | { 10 | var dishForCreationDto = context.GetArgument(2); 11 | 12 | if (!MiniValidator.TryValidate(dishForCreationDto, out var validationErrors)) 13 | { 14 | return TypedResults.ValidationProblem(validationErrors); 15 | } 16 | 17 | return await next(context); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/EndpointHandlers/DishesHandlers.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DishesAPI.DbContexts; 3 | using DishesAPI.Entities; 4 | using DishesAPI.Models; 5 | using Microsoft.AspNetCore.Http.HttpResults; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.EntityFrameworkCore; 8 | using System.Security.Claims; 9 | 10 | namespace DishesAPI.EndpointHandlers; 11 | 12 | public static class DishesHandlers 13 | { 14 | public static async Task>> GetDishesAsync( 15 | DishesDbContext dishesDbContext, 16 | ClaimsPrincipal claimsPrincipal, 17 | IMapper mapper, 18 | ILogger logger, 19 | string? name) 20 | { 21 | Console.WriteLine($"User authenticated? {claimsPrincipal.Identity?.IsAuthenticated}"); 22 | 23 | logger.LogInformation("Getting the dishes..."); 24 | 25 | return TypedResults.Ok(mapper.Map>(await dishesDbContext.Dishes 26 | .Where(d => name == null || d.Name.Contains(name)) 27 | .ToListAsync())); 28 | } 29 | 30 | public static async Task>> GetDishByIdAsync(DishesDbContext dishesDbContext, 31 | IMapper mapper, 32 | Guid dishId) 33 | { 34 | var dishEntity = await dishesDbContext.Dishes.FirstOrDefaultAsync(d => d.Id == dishId); 35 | if (dishEntity == null) 36 | { 37 | return TypedResults.NotFound(); 38 | } 39 | 40 | return TypedResults.Ok(mapper.Map(dishEntity)); 41 | } 42 | 43 | public static async Task> GetDishByNameAsync(DishesDbContext dishesDbContext, 44 | IMapper mapper, 45 | string dishName) 46 | { 47 | return TypedResults.Ok(mapper.Map(await dishesDbContext.Dishes.FirstOrDefaultAsync(d => d.Name == dishName))); 48 | } 49 | 50 | public static async Task> CreateDishAsync(DishesDbContext dishesDbContext, 51 | IMapper mapper, 52 | DishForCreationDto dishForCreationDto 53 | //HttpContext httpContext, 54 | //LinkGenerator linkGenerator 55 | ) 56 | { 57 | var dishEntity = mapper.Map(dishForCreationDto); 58 | dishesDbContext.Add(dishEntity); 59 | await dishesDbContext.SaveChangesAsync(); 60 | 61 | var dishToReturn = mapper.Map(dishEntity); 62 | 63 | return TypedResults.CreatedAtRoute( 64 | dishToReturn, 65 | "GetDish", 66 | new 67 | { 68 | dishId = dishToReturn.Id 69 | }); 70 | 71 | //var linkToDish = linkGenerator.GetUriByName( 72 | // httpContext, 73 | // "GetDish", 74 | // new { dishId = dishToReturn.Id }); 75 | 76 | // return TypedResults.Created(linkToDish, dishToReturn); 77 | } 78 | 79 | public static async Task> UpdateDishAsync 80 | (DishesDbContext dishesDbContext, 81 | IMapper mapper, 82 | Guid dishId, 83 | DishForUpdateDto dishForUpdateDto) 84 | { 85 | var dishEntity = await dishesDbContext.Dishes.FirstOrDefaultAsync(d => d.Id == dishId); 86 | if (dishEntity == null) 87 | { 88 | return TypedResults.NotFound(); 89 | } 90 | 91 | mapper.Map(dishForUpdateDto, dishEntity); 92 | await dishesDbContext.SaveChangesAsync(); 93 | return TypedResults.NoContent(); 94 | } 95 | 96 | public static async Task> DeleteDishAsync(DishesDbContext dishesDbContext, 97 | Guid dishId) 98 | { 99 | var dishEntity = await dishesDbContext.Dishes.FirstOrDefaultAsync(d => d.Id == dishId); 100 | if (dishEntity == null) 101 | { 102 | return TypedResults.NotFound(); 103 | } 104 | 105 | dishesDbContext.Dishes.Remove(dishEntity); 106 | await dishesDbContext.SaveChangesAsync(); 107 | return TypedResults.NoContent(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/EndpointHandlers/IngredientsHandlers.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DishesAPI.DbContexts; 3 | using DishesAPI.Models; 4 | using Microsoft.AspNetCore.Http.HttpResults; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace DishesAPI.EndpointHandlers; 8 | 9 | public class IngredientsHandlers 10 | { 11 | public static async Task>>> GetIngredientsAsync( 12 | DishesDbContext dishesDbContext, 13 | IMapper mapper, 14 | Guid dishId) 15 | { 16 | var dishEntity = await dishesDbContext.Dishes.FirstOrDefaultAsync(d => d.Id == dishId); 17 | if (dishEntity == null) 18 | { 19 | return TypedResults.NotFound(); 20 | } 21 | 22 | return TypedResults.Ok(mapper.Map>((await dishesDbContext.Dishes 23 | .Include(d => d.Ingredients) 24 | .FirstOrDefaultAsync(d => d.Id == dishId))?.Ingredients)); 25 | } 26 | } -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Entities/Dish.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace DishesAPI.Entities; 6 | 7 | public class Dish 8 | { 9 | [Key] 10 | public Guid Id { get; set; } 11 | 12 | // "required" modifier: compiler guarantees Name is initialized when the Dish is instantiated. 13 | // see: https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types 14 | // & https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#required-members 15 | [Required] 16 | [MaxLength(200)] 17 | public required string Name { get; set; } 18 | 19 | public ICollection Ingredients { get; set; } = new List(); 20 | 21 | public Dish() 22 | { 23 | } 24 | 25 | [SetsRequiredMembers] 26 | public Dish(Guid id, string name) 27 | { 28 | Id = id; 29 | Name = name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Entities/Ingredient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query.Internal; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace DishesAPI.Entities; 6 | 7 | public class Ingredient 8 | { 9 | [Key] 10 | public Guid Id { get; set; } 11 | 12 | [Required] 13 | [MaxLength(200)] 14 | public required string Name { get; set; } 15 | 16 | public ICollection Dishes { get; set; } = new List(); 17 | 18 | public Ingredient() 19 | { } 20 | 21 | [SetsRequiredMembers] 22 | public Ingredient(Guid id, string name) 23 | { 24 | Id = id; 25 | Name = name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Extensions/EndpointRouteBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using DishesAPI.EndpointFilters; 2 | using DishesAPI.EndpointHandlers; 3 | using DishesAPI.Models; 4 | 5 | namespace DishesAPI.Extensions; 6 | 7 | public static class EndpointRouteBuilderExtensions 8 | { 9 | public static void RegisterDishesEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) 10 | { 11 | var dishesEndpoints = endpointRouteBuilder.MapGroup("/dishes") 12 | .RequireAuthorization(); 13 | var dishWithGuidIdEndpoints = dishesEndpoints.MapGroup("/{dishId:guid}"); 14 | var dishWithGuidIdEndpointsAndLockFilters = 15 | endpointRouteBuilder.MapGroup("/dishes/{dishId:guid}") 16 | .RequireAuthorization("RequireAdminFromBelgium") 17 | .AddEndpointFilter(new DishIsLockedFilter( 18 | new("fd630a57-2352-4731-b25c-db9cc7601b16"))) 19 | .AddEndpointFilter(new DishIsLockedFilter( 20 | new("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"))); 21 | 22 | dishesEndpoints.MapGet("", DishesHandlers.GetDishesAsync); 23 | dishWithGuidIdEndpoints.MapGet("", DishesHandlers.GetDishByIdAsync) 24 | .WithName("GetDish") 25 | .WithOpenApi() 26 | .WithSummary("Get a dish by providing an id.") 27 | .WithDescription("Dishes are identified by a URI containing a dish identifier. This identifier is a GUID. You can get one specific dish via this endpoint by providing the identifier. "); 28 | dishesEndpoints.MapGet("/{dishName}", DishesHandlers.GetDishByNameAsync) 29 | .AllowAnonymous() 30 | .WithOpenApi(operation => 31 | { 32 | operation.Deprecated = true; 33 | return operation; 34 | }); 35 | dishesEndpoints.MapPost("", DishesHandlers.CreateDishAsync) 36 | .RequireAuthorization("RequireAdminFromBelgium") 37 | .AddEndpointFilter() 38 | .ProducesValidationProblem(400) 39 | .Accepts( 40 | "application/json", 41 | "application/vnd.marvin.dishforcreation+json"); 42 | dishWithGuidIdEndpointsAndLockFilters.MapPut("", DishesHandlers.UpdateDishAsync); 43 | dishWithGuidIdEndpointsAndLockFilters.MapDelete("", DishesHandlers.DeleteDishAsync) 44 | .AddEndpointFilter(); 45 | } 46 | 47 | public static void RegisterIngredientsEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) 48 | { 49 | var ingredientsEndpoints = endpointRouteBuilder.MapGroup("/dishes/{dishId:guid}/ingredients").RequireAuthorization(); 50 | 51 | ingredientsEndpoints.MapGet("", IngredientsHandlers.GetIngredientsAsync); 52 | ingredientsEndpoints.MapPost("", () => 53 | { 54 | throw new NotImplementedException(); 55 | }); 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Migrations/20230314101754_InitialMigration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DishesAPI.DbContexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace DishesAPI.Migrations 12 | { 13 | [DbContext(typeof(DishesDbContext))] 14 | [Migration("20230314101754_InitialMigration")] 15 | partial class InitialMigration 16 | { 17 | /// 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder.HasAnnotation("ProductVersion", "7.0.3"); 22 | 23 | modelBuilder.Entity("DishIngredient", b => 24 | { 25 | b.Property("DishesId") 26 | .HasColumnType("TEXT"); 27 | 28 | b.Property("IngredientsId") 29 | .HasColumnType("TEXT"); 30 | 31 | b.HasKey("DishesId", "IngredientsId"); 32 | 33 | b.HasIndex("IngredientsId"); 34 | 35 | b.ToTable("DishIngredient"); 36 | 37 | b.HasData( 38 | new 39 | { 40 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 41 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 42 | }, 43 | new 44 | { 45 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 46 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 47 | }, 48 | new 49 | { 50 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 51 | IngredientsId = new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40") 52 | }, 53 | new 54 | { 55 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 56 | IngredientsId = new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b") 57 | }, 58 | new 59 | { 60 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 61 | IngredientsId = new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6") 62 | }, 63 | new 64 | { 65 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 66 | IngredientsId = new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc") 67 | }, 68 | new 69 | { 70 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 71 | IngredientsId = new Guid("b5f336e2-c226-4389-aac3-2499325a3de9") 72 | }, 73 | new 74 | { 75 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 76 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 77 | }, 78 | new 79 | { 80 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 81 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 82 | }, 83 | new 84 | { 85 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 86 | IngredientsId = new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d") 87 | }, 88 | new 89 | { 90 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 91 | IngredientsId = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") 92 | }, 93 | new 94 | { 95 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 96 | IngredientsId = new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1") 97 | }, 98 | new 99 | { 100 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 101 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 102 | }, 103 | new 104 | { 105 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 106 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 107 | }, 108 | new 109 | { 110 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 111 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 112 | }, 113 | new 114 | { 115 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 116 | IngredientsId = new Guid("40563e5b-e538-4084-9587-3df74fae21d4") 117 | }, 118 | new 119 | { 120 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 121 | IngredientsId = new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b") 122 | }, 123 | new 124 | { 125 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 126 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 127 | }, 128 | new 129 | { 130 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 131 | IngredientsId = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") 132 | }, 133 | new 134 | { 135 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 136 | IngredientsId = new Guid("b617df23-3d91-40e1-99aa-b07d264aa937") 137 | }, 138 | new 139 | { 140 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 141 | IngredientsId = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") 142 | }, 143 | new 144 | { 145 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 146 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 147 | }, 148 | new 149 | { 150 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 151 | IngredientsId = new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3") 152 | }, 153 | new 154 | { 155 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 156 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 157 | }, 158 | new 159 | { 160 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 161 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 162 | }, 163 | new 164 | { 165 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 166 | IngredientsId = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") 167 | }, 168 | new 169 | { 170 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 171 | IngredientsId = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") 172 | }, 173 | new 174 | { 175 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 176 | IngredientsId = new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7") 177 | }, 178 | new 179 | { 180 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 181 | IngredientsId = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") 182 | }, 183 | new 184 | { 185 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 186 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 187 | }, 188 | new 189 | { 190 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 191 | IngredientsId = new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e") 192 | }, 193 | new 194 | { 195 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 196 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 197 | }, 198 | new 199 | { 200 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 201 | IngredientsId = new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10") 202 | }, 203 | new 204 | { 205 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 206 | IngredientsId = new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4") 207 | }, 208 | new 209 | { 210 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 211 | IngredientsId = new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c") 212 | }, 213 | new 214 | { 215 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 216 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 217 | }, 218 | new 219 | { 220 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 221 | IngredientsId = new Guid("40563e5b-e538-4084-9587-3df74fae21d4") 222 | }, 223 | new 224 | { 225 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 226 | IngredientsId = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") 227 | }, 228 | new 229 | { 230 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 231 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 232 | }, 233 | new 234 | { 235 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 236 | IngredientsId = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") 237 | }, 238 | new 239 | { 240 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 241 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 242 | }); 243 | }); 244 | 245 | modelBuilder.Entity("DishesAPI.Entities.Dish", b => 246 | { 247 | b.Property("Id") 248 | .ValueGeneratedOnAdd() 249 | .HasColumnType("TEXT"); 250 | 251 | b.Property("Name") 252 | .IsRequired() 253 | .HasMaxLength(200) 254 | .HasColumnType("TEXT"); 255 | 256 | b.HasKey("Id"); 257 | 258 | b.ToTable("Dishes"); 259 | 260 | b.HasData( 261 | new 262 | { 263 | Id = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 264 | Name = "Flemish Beef stew with chicory" 265 | }, 266 | new 267 | { 268 | Id = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 269 | Name = "Mussels with french fries" 270 | }, 271 | new 272 | { 273 | Id = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 274 | Name = "Ragu alla bolognaise" 275 | }, 276 | new 277 | { 278 | Id = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 279 | Name = "Rendang" 280 | }, 281 | new 282 | { 283 | Id = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 284 | Name = "Fish Masala" 285 | }); 286 | }); 287 | 288 | modelBuilder.Entity("DishesAPI.Entities.Ingredient", b => 289 | { 290 | b.Property("Id") 291 | .ValueGeneratedOnAdd() 292 | .HasColumnType("TEXT"); 293 | 294 | b.Property("Name") 295 | .IsRequired() 296 | .HasMaxLength(200) 297 | .HasColumnType("TEXT"); 298 | 299 | b.HasKey("Id"); 300 | 301 | b.ToTable("Ingredients"); 302 | 303 | b.HasData( 304 | new 305 | { 306 | Id = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35"), 307 | Name = "Beef" 308 | }, 309 | new 310 | { 311 | Id = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96"), 312 | Name = "Onion" 313 | }, 314 | new 315 | { 316 | Id = new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40"), 317 | Name = "Dark beer" 318 | }, 319 | new 320 | { 321 | Id = new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b"), 322 | Name = "Brown piece of bread" 323 | }, 324 | new 325 | { 326 | Id = new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6"), 327 | Name = "Mustard" 328 | }, 329 | new 330 | { 331 | Id = new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc"), 332 | Name = "Chicory" 333 | }, 334 | new 335 | { 336 | Id = new Guid("b5f336e2-c226-4389-aac3-2499325a3de9"), 337 | Name = "Mayo" 338 | }, 339 | new 340 | { 341 | Id = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe"), 342 | Name = "Various spices" 343 | }, 344 | new 345 | { 346 | Id = new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d"), 347 | Name = "Mussels" 348 | }, 349 | new 350 | { 351 | Id = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f"), 352 | Name = "Celery" 353 | }, 354 | new 355 | { 356 | Id = new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1"), 357 | Name = "French fries" 358 | }, 359 | new 360 | { 361 | Id = new Guid("40563e5b-e538-4084-9587-3df74fae21d4"), 362 | Name = "Tomato" 363 | }, 364 | new 365 | { 366 | Id = new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b"), 367 | Name = "Tomato paste" 368 | }, 369 | new 370 | { 371 | Id = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d"), 372 | Name = "Bay leave" 373 | }, 374 | new 375 | { 376 | Id = new Guid("b617df23-3d91-40e1-99aa-b07d264aa937"), 377 | Name = "Carrot" 378 | }, 379 | new 380 | { 381 | Id = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56"), 382 | Name = "Garlic" 383 | }, 384 | new 385 | { 386 | Id = new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3"), 387 | Name = "Red wine" 388 | }, 389 | new 390 | { 391 | Id = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671"), 392 | Name = "Coconut milk" 393 | }, 394 | new 395 | { 396 | Id = new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7"), 397 | Name = "Ginger" 398 | }, 399 | new 400 | { 401 | Id = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237"), 402 | Name = "Chili pepper" 403 | }, 404 | new 405 | { 406 | Id = new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e"), 407 | Name = "Tamarind paste" 408 | }, 409 | new 410 | { 411 | Id = new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10"), 412 | Name = "Firm fish" 413 | }, 414 | new 415 | { 416 | Id = new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4"), 417 | Name = "Ginger garlic paste" 418 | }, 419 | new 420 | { 421 | Id = new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c"), 422 | Name = "Garam masala" 423 | }); 424 | }); 425 | 426 | modelBuilder.Entity("DishIngredient", b => 427 | { 428 | b.HasOne("DishesAPI.Entities.Dish", null) 429 | .WithMany() 430 | .HasForeignKey("DishesId") 431 | .OnDelete(DeleteBehavior.Cascade) 432 | .IsRequired(); 433 | 434 | b.HasOne("DishesAPI.Entities.Ingredient", null) 435 | .WithMany() 436 | .HasForeignKey("IngredientsId") 437 | .OnDelete(DeleteBehavior.Cascade) 438 | .IsRequired(); 439 | }); 440 | #pragma warning restore 612, 618 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Migrations/20230314101754_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | #pragma warning disable CA1814 // Prefer jagged arrays over multidimensional 7 | 8 | namespace DishesAPI.Migrations 9 | { 10 | /// 11 | public partial class InitialMigration : Migration 12 | { 13 | /// 14 | protected override void Up(MigrationBuilder migrationBuilder) 15 | { 16 | migrationBuilder.CreateTable( 17 | name: "Dishes", 18 | columns: table => new 19 | { 20 | Id = table.Column(type: "TEXT", nullable: false), 21 | Name = table.Column(type: "TEXT", maxLength: 200, nullable: false) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Dishes", x => x.Id); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "Ingredients", 30 | columns: table => new 31 | { 32 | Id = table.Column(type: "TEXT", nullable: false), 33 | Name = table.Column(type: "TEXT", maxLength: 200, nullable: false) 34 | }, 35 | constraints: table => 36 | { 37 | table.PrimaryKey("PK_Ingredients", x => x.Id); 38 | }); 39 | 40 | migrationBuilder.CreateTable( 41 | name: "DishIngredient", 42 | columns: table => new 43 | { 44 | DishesId = table.Column(type: "TEXT", nullable: false), 45 | IngredientsId = table.Column(type: "TEXT", nullable: false) 46 | }, 47 | constraints: table => 48 | { 49 | table.PrimaryKey("PK_DishIngredient", x => new { x.DishesId, x.IngredientsId }); 50 | table.ForeignKey( 51 | name: "FK_DishIngredient_Dishes_DishesId", 52 | column: x => x.DishesId, 53 | principalTable: "Dishes", 54 | principalColumn: "Id", 55 | onDelete: ReferentialAction.Cascade); 56 | table.ForeignKey( 57 | name: "FK_DishIngredient_Ingredients_IngredientsId", 58 | column: x => x.IngredientsId, 59 | principalTable: "Ingredients", 60 | principalColumn: "Id", 61 | onDelete: ReferentialAction.Cascade); 62 | }); 63 | 64 | migrationBuilder.InsertData( 65 | table: "Dishes", 66 | columns: new[] { "Id", "Name" }, 67 | values: new object[,] 68 | { 69 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), "Fish Masala" }, 70 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), "Ragu alla bolognaise" }, 71 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), "Flemish Beef stew with chicory" }, 72 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), "Rendang" }, 73 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), "Mussels with french fries" } 74 | }); 75 | 76 | migrationBuilder.InsertData( 77 | table: "Ingredients", 78 | columns: new[] { "Id", "Name" }, 79 | values: new object[,] 80 | { 81 | { new Guid("047ab5cc-d041-486e-9d22-a0860fb13237"), "Chili pepper" }, 82 | { new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b"), "Brown piece of bread" }, 83 | { new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7"), "Ginger" }, 84 | { new Guid("40563e5b-e538-4084-9587-3df74fae21d4"), "Tomato" }, 85 | { new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc"), "Chicory" }, 86 | { new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1"), "French fries" }, 87 | { new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6"), "Mustard" }, 88 | { new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4"), "Ginger garlic paste" }, 89 | { new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d"), "Mussels" }, 90 | { new Guid("b5f336e2-c226-4389-aac3-2499325a3de9"), "Mayo" }, 91 | { new Guid("b617df23-3d91-40e1-99aa-b07d264aa937"), "Carrot" }, 92 | { new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56"), "Garlic" }, 93 | { new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40"), "Dark beer" }, 94 | { new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe"), "Various spices" }, 95 | { new Guid("c2c75b40-2453-416e-a7ed-3505b121d671"), "Coconut milk" }, 96 | { new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10"), "Firm fish" }, 97 | { new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35"), "Beef" }, 98 | { new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d"), "Bay leave" }, 99 | { new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96"), "Onion" }, 100 | { new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e"), "Tamarind paste" }, 101 | { new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c"), "Garam masala" }, 102 | { new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3"), "Red wine" }, 103 | { new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b"), "Tomato paste" }, 104 | { new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f"), "Celery" } 105 | }); 106 | 107 | migrationBuilder.InsertData( 108 | table: "DishIngredient", 109 | columns: new[] { "DishesId", "IngredientsId" }, 110 | values: new object[,] 111 | { 112 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") }, 113 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("40563e5b-e538-4084-9587-3df74fae21d4") }, 114 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4") }, 115 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 116 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") }, 117 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10") }, 118 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 119 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 120 | { new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c") }, 121 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("40563e5b-e538-4084-9587-3df74fae21d4") }, 122 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("b617df23-3d91-40e1-99aa-b07d264aa937") }, 123 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 124 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 125 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 126 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 127 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 128 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3") }, 129 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b") }, 130 | { new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") }, 131 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b") }, 132 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc") }, 133 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6") }, 134 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("b5f336e2-c226-4389-aac3-2499325a3de9") }, 135 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40") }, 136 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 137 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 138 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 139 | { new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 140 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") }, 141 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7") }, 142 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 143 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 144 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") }, 145 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 146 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 147 | { new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e") }, 148 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1") }, 149 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d") }, 150 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 151 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 152 | { new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") } 153 | }); 154 | 155 | migrationBuilder.CreateIndex( 156 | name: "IX_DishIngredient_IngredientsId", 157 | table: "DishIngredient", 158 | column: "IngredientsId"); 159 | } 160 | 161 | /// 162 | protected override void Down(MigrationBuilder migrationBuilder) 163 | { 164 | migrationBuilder.DropTable( 165 | name: "DishIngredient"); 166 | 167 | migrationBuilder.DropTable( 168 | name: "Dishes"); 169 | 170 | migrationBuilder.DropTable( 171 | name: "Ingredients"); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Migrations/DishesDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DishesAPI.DbContexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | #nullable disable 9 | 10 | namespace DishesAPI.Migrations 11 | { 12 | [DbContext(typeof(DishesDbContext))] 13 | partial class DishesDbContextModelSnapshot : ModelSnapshot 14 | { 15 | protected override void BuildModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder.HasAnnotation("ProductVersion", "7.0.3"); 19 | 20 | modelBuilder.Entity("DishIngredient", b => 21 | { 22 | b.Property("DishesId") 23 | .HasColumnType("TEXT"); 24 | 25 | b.Property("IngredientsId") 26 | .HasColumnType("TEXT"); 27 | 28 | b.HasKey("DishesId", "IngredientsId"); 29 | 30 | b.HasIndex("IngredientsId"); 31 | 32 | b.ToTable("DishIngredient"); 33 | 34 | b.HasData( 35 | new 36 | { 37 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 38 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 39 | }, 40 | new 41 | { 42 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 43 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 44 | }, 45 | new 46 | { 47 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 48 | IngredientsId = new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40") 49 | }, 50 | new 51 | { 52 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 53 | IngredientsId = new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b") 54 | }, 55 | new 56 | { 57 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 58 | IngredientsId = new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6") 59 | }, 60 | new 61 | { 62 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 63 | IngredientsId = new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc") 64 | }, 65 | new 66 | { 67 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 68 | IngredientsId = new Guid("b5f336e2-c226-4389-aac3-2499325a3de9") 69 | }, 70 | new 71 | { 72 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 73 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 74 | }, 75 | new 76 | { 77 | DishesId = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 78 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 79 | }, 80 | new 81 | { 82 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 83 | IngredientsId = new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d") 84 | }, 85 | new 86 | { 87 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 88 | IngredientsId = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") 89 | }, 90 | new 91 | { 92 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 93 | IngredientsId = new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1") 94 | }, 95 | new 96 | { 97 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 98 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 99 | }, 100 | new 101 | { 102 | DishesId = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 103 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 104 | }, 105 | new 106 | { 107 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 108 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 109 | }, 110 | new 111 | { 112 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 113 | IngredientsId = new Guid("40563e5b-e538-4084-9587-3df74fae21d4") 114 | }, 115 | new 116 | { 117 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 118 | IngredientsId = new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b") 119 | }, 120 | new 121 | { 122 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 123 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 124 | }, 125 | new 126 | { 127 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 128 | IngredientsId = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") 129 | }, 130 | new 131 | { 132 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 133 | IngredientsId = new Guid("b617df23-3d91-40e1-99aa-b07d264aa937") 134 | }, 135 | new 136 | { 137 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 138 | IngredientsId = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") 139 | }, 140 | new 141 | { 142 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 143 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 144 | }, 145 | new 146 | { 147 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 148 | IngredientsId = new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3") 149 | }, 150 | new 151 | { 152 | DishesId = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 153 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 154 | }, 155 | new 156 | { 157 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 158 | IngredientsId = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35") 159 | }, 160 | new 161 | { 162 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 163 | IngredientsId = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") 164 | }, 165 | new 166 | { 167 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 168 | IngredientsId = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") 169 | }, 170 | new 171 | { 172 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 173 | IngredientsId = new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7") 174 | }, 175 | new 176 | { 177 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 178 | IngredientsId = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") 179 | }, 180 | new 181 | { 182 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 183 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 184 | }, 185 | new 186 | { 187 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 188 | IngredientsId = new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e") 189 | }, 190 | new 191 | { 192 | DishesId = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 193 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 194 | }, 195 | new 196 | { 197 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 198 | IngredientsId = new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10") 199 | }, 200 | new 201 | { 202 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 203 | IngredientsId = new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4") 204 | }, 205 | new 206 | { 207 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 208 | IngredientsId = new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c") 209 | }, 210 | new 211 | { 212 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 213 | IngredientsId = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96") 214 | }, 215 | new 216 | { 217 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 218 | IngredientsId = new Guid("40563e5b-e538-4084-9587-3df74fae21d4") 219 | }, 220 | new 221 | { 222 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 223 | IngredientsId = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671") 224 | }, 225 | new 226 | { 227 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 228 | IngredientsId = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d") 229 | }, 230 | new 231 | { 232 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 233 | IngredientsId = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237") 234 | }, 235 | new 236 | { 237 | DishesId = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 238 | IngredientsId = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe") 239 | }); 240 | }); 241 | 242 | modelBuilder.Entity("DishesAPI.Entities.Dish", b => 243 | { 244 | b.Property("Id") 245 | .ValueGeneratedOnAdd() 246 | .HasColumnType("TEXT"); 247 | 248 | b.Property("Name") 249 | .IsRequired() 250 | .HasMaxLength(200) 251 | .HasColumnType("TEXT"); 252 | 253 | b.HasKey("Id"); 254 | 255 | b.ToTable("Dishes"); 256 | 257 | b.HasData( 258 | new 259 | { 260 | Id = new Guid("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 261 | Name = "Flemish Beef stew with chicory" 262 | }, 263 | new 264 | { 265 | Id = new Guid("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 266 | Name = "Mussels with french fries" 267 | }, 268 | new 269 | { 270 | Id = new Guid("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 271 | Name = "Ragu alla bolognaise" 272 | }, 273 | new 274 | { 275 | Id = new Guid("fd630a57-2352-4731-b25c-db9cc7601b16"), 276 | Name = "Rendang" 277 | }, 278 | new 279 | { 280 | Id = new Guid("98929bd4-f099-41eb-a994-f1918b724b5a"), 281 | Name = "Fish Masala" 282 | }); 283 | }); 284 | 285 | modelBuilder.Entity("DishesAPI.Entities.Ingredient", b => 286 | { 287 | b.Property("Id") 288 | .ValueGeneratedOnAdd() 289 | .HasColumnType("TEXT"); 290 | 291 | b.Property("Name") 292 | .IsRequired() 293 | .HasMaxLength(200) 294 | .HasColumnType("TEXT"); 295 | 296 | b.HasKey("Id"); 297 | 298 | b.ToTable("Ingredients"); 299 | 300 | b.HasData( 301 | new 302 | { 303 | Id = new Guid("d28888e9-2ba9-473a-a40f-e38cb54f9b35"), 304 | Name = "Beef" 305 | }, 306 | new 307 | { 308 | Id = new Guid("da2fd609-d754-4feb-8acd-c4f9ff13ba96"), 309 | Name = "Onion" 310 | }, 311 | new 312 | { 313 | Id = new Guid("c19099ed-94db-44ba-885b-0ad7205d5e40"), 314 | Name = "Dark beer" 315 | }, 316 | new 317 | { 318 | Id = new Guid("0c4dc798-b38b-4a1c-905c-a9e76dbef17b"), 319 | Name = "Brown piece of bread" 320 | }, 321 | new 322 | { 323 | Id = new Guid("937b1ba1-7969-4324-9ab5-afb0e4d875e6"), 324 | Name = "Mustard" 325 | }, 326 | new 327 | { 328 | Id = new Guid("7a2fbc72-bb33-49de-bd23-c78fceb367fc"), 329 | Name = "Chicory" 330 | }, 331 | new 332 | { 333 | Id = new Guid("b5f336e2-c226-4389-aac3-2499325a3de9"), 334 | Name = "Mayo" 335 | }, 336 | new 337 | { 338 | Id = new Guid("c22bec27-a880-4f2a-b380-12dcd99c61fe"), 339 | Name = "Various spices" 340 | }, 341 | new 342 | { 343 | Id = new Guid("aab31c70-57ce-4b6d-a66c-9c1b094e915d"), 344 | Name = "Mussels" 345 | }, 346 | new 347 | { 348 | Id = new Guid("fef8b722-664d-403f-ae3c-05f8ed7d7a1f"), 349 | Name = "Celery" 350 | }, 351 | new 352 | { 353 | Id = new Guid("8d5a1b40-6677-4545-b6e8-5ba93efda0a1"), 354 | Name = "French fries" 355 | }, 356 | new 357 | { 358 | Id = new Guid("40563e5b-e538-4084-9587-3df74fae21d4"), 359 | Name = "Tomato" 360 | }, 361 | new 362 | { 363 | Id = new Guid("f350e1a0-38de-42fe-ada5-ae436378ee5b"), 364 | Name = "Tomato paste" 365 | }, 366 | new 367 | { 368 | Id = new Guid("d5cad9a4-144e-4a3d-858d-9840792fa65d"), 369 | Name = "Bay leave" 370 | }, 371 | new 372 | { 373 | Id = new Guid("b617df23-3d91-40e1-99aa-b07d264aa937"), 374 | Name = "Carrot" 375 | }, 376 | new 377 | { 378 | Id = new Guid("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56"), 379 | Name = "Garlic" 380 | }, 381 | new 382 | { 383 | Id = new Guid("ecd396c3-4403-4fbf-83ca-94a8e9d859b3"), 384 | Name = "Red wine" 385 | }, 386 | new 387 | { 388 | Id = new Guid("c2c75b40-2453-416e-a7ed-3505b121d671"), 389 | Name = "Coconut milk" 390 | }, 391 | new 392 | { 393 | Id = new Guid("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7"), 394 | Name = "Ginger" 395 | }, 396 | new 397 | { 398 | Id = new Guid("047ab5cc-d041-486e-9d22-a0860fb13237"), 399 | Name = "Chili pepper" 400 | }, 401 | new 402 | { 403 | Id = new Guid("e0017fe1-773f-4a59-9730-9489833c6e8e"), 404 | Name = "Tamarind paste" 405 | }, 406 | new 407 | { 408 | Id = new Guid("c9b46f9c-d6ce-42c3-8736-2cddbbadee10"), 409 | Name = "Firm fish" 410 | }, 411 | new 412 | { 413 | Id = new Guid("a07cde83-3127-45da-bbd5-04a7c8d13aa4"), 414 | Name = "Ginger garlic paste" 415 | }, 416 | new 417 | { 418 | Id = new Guid("ebe94d5d-2ad8-4886-b246-05a1fad83d1c"), 419 | Name = "Garam masala" 420 | }); 421 | }); 422 | 423 | modelBuilder.Entity("DishIngredient", b => 424 | { 425 | b.HasOne("DishesAPI.Entities.Dish", null) 426 | .WithMany() 427 | .HasForeignKey("DishesId") 428 | .OnDelete(DeleteBehavior.Cascade) 429 | .IsRequired(); 430 | 431 | b.HasOne("DishesAPI.Entities.Ingredient", null) 432 | .WithMany() 433 | .HasForeignKey("IngredientsId") 434 | .OnDelete(DeleteBehavior.Cascade) 435 | .IsRequired(); 436 | }); 437 | #pragma warning restore 612, 618 438 | } 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Models/DishDto.cs: -------------------------------------------------------------------------------- 1 | namespace DishesAPI.Models; 2 | 3 | public class DishDto 4 | { 5 | public Guid Id { get; set; } 6 | 7 | public required string Name { get; set; } 8 | } -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Models/DishForCreationDto.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace DishesAPI.Models; 4 | 5 | public class DishForCreationDto 6 | { 7 | [Required] 8 | [StringLength(100, MinimumLength = 3)] 9 | public required string Name { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Models/DishForUpdateDto.cs: -------------------------------------------------------------------------------- 1 | namespace DishesAPI.Models; 2 | 3 | public class DishForUpdateDto 4 | { 5 | public required string Name { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Models/IngredientDto.cs: -------------------------------------------------------------------------------- 1 | namespace DishesAPI.Models; 2 | 3 | public class IngredientDto 4 | { 5 | public Guid Id { get; set; } 6 | 7 | public required string Name { get; set; } 8 | 9 | public Guid DishId { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Profiles/DishProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DishesAPI.Entities; 3 | using DishesAPI.Models; 4 | 5 | namespace DishesAPI.Profiles; 6 | 7 | public class DishProfile : Profile 8 | { 9 | public DishProfile() 10 | { 11 | CreateMap(); 12 | CreateMap(); 13 | CreateMap(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Profiles/IngredientProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using DishesAPI.Entities; 3 | using DishesAPI.Models; 4 | 5 | namespace DishesAPI.Profiles; 6 | 7 | public class IngredientProfile : Profile 8 | { 9 | public IngredientProfile() 10 | { 11 | CreateMap() 12 | .ForMember( 13 | d => d.DishId, 14 | o => o.MapFrom(s => s.Dishes.First().Id)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using DishesAPI.DbContexts; 2 | using DishesAPI.Extensions; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.OpenApi.Models; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // Add services to the container. 9 | 10 | // register the DbContext on the container, getting the 11 | // connection string from appSettings 12 | builder.Services.AddDbContext(o => o.UseSqlite( 13 | builder.Configuration["ConnectionStrings:DishesDBConnectionString"])); 14 | 15 | builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); 16 | 17 | builder.Services.AddProblemDetails(); 18 | 19 | builder.Services.AddAuthentication().AddJwtBearer(); 20 | builder.Services.AddAuthorization(); 21 | 22 | builder.Services.AddAuthorizationBuilder() 23 | .AddPolicy("RequireAdminFromBelgium", policy => 24 | policy 25 | .RequireRole("admin") 26 | .RequireClaim("country", "Belgium")); 27 | 28 | builder.Services.AddEndpointsApiExplorer(); 29 | builder.Services.AddSwaggerGen(options => 30 | { 31 | options.AddSecurityDefinition("TokenAuthNZ", 32 | new() 33 | { 34 | Name = "Authorization", 35 | Description = "Token-based authentication and authorization", 36 | Type = SecuritySchemeType.Http, 37 | Scheme = "Bearer", 38 | In = ParameterLocation.Header 39 | }); 40 | options.AddSecurityRequirement(new() 41 | { 42 | { 43 | new () 44 | { 45 | Reference = new OpenApiReference { 46 | Type = ReferenceType.SecurityScheme, 47 | Id = "TokenAuthNZ" } 48 | }, new List()} 49 | }); 50 | }); 51 | 52 | var app = builder.Build(); 53 | 54 | // Configure the HTTP request pipeline. 55 | if (!app.Environment.IsDevelopment()) 56 | { 57 | app.UseExceptionHandler(); 58 | //app.UseExceptionHandler(configureApplicationBuilder => 59 | //{ 60 | // configureApplicationBuilder.Run( 61 | // async context => 62 | // { 63 | // context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; 64 | // context.Response.ContentType = "text/html"; 65 | // await context.Response.WriteAsync("An unexpected problem happened."); 66 | // }); 67 | 68 | //}); 69 | } 70 | app.UseHttpsRedirection(); 71 | 72 | app.UseSwagger(); 73 | app.UseSwaggerUI(); 74 | 75 | app.UseAuthentication(); 76 | app.UseAuthorization(); 77 | 78 | app.RegisterDishesEndpoints(); 79 | app.RegisterIngredientsEndpoints(); 80 | 81 | // recreate & migrate the database on each run, for demo purposes 82 | using (var serviceScope = app.Services.GetService().CreateScope()) 83 | { 84 | var context = serviceScope.ServiceProvider.GetRequiredService(); 85 | context.Database.EnsureDeleted(); 86 | context.Database.Migrate(); 87 | } 88 | 89 | app.Run(); -------------------------------------------------------------------------------- /Finished sample/DishesAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:6079", 8 | "sslPort": 44393 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "weatherforecast", 17 | "applicationUrl": "http://localhost:5034", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": false, 26 | "applicationUrl": "https://localhost:7077;http://localhost:5034", 27 | "environmentVariables": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | } 30 | }, 31 | "IIS Express": { 32 | "commandName": "IISExpress", 33 | "launchBrowser": true, 34 | "launchUrl": "weatherforecast", 35 | "environmentVariables": { 36 | "ASPNETCORE_ENVIRONMENT": "Development" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Finished sample/DishesAPI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "Authentication": { 9 | "Schemes": { 10 | "Bearer": { 11 | "ValidAudiences": [ 12 | "menu-api" 13 | ], 14 | "ValidIssuer": "dotnet-user-jwts" 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Finished sample/DishesAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "ConnectionStrings": { 9 | "DishesDBConnectionString": "Data Source=Dishes.db" 10 | }, 11 | "AllowedHosts": "*", 12 | "Authentication": { 13 | "DefaultScheme": "Bearer", 14 | "Schemes": { 15 | "Bearer": { 16 | "ValidAudiences": [ 17 | "menu-api" 18 | ], 19 | "ValidIssuer": "dotnet-user-jwts" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kevin Dockx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building ASP.NET Core 7 Minimal APIs 2 | Fully functioning sample code for my "Building ASP.NET Core 7 Minimal APIs" course. 3 | -------------------------------------------------------------------------------- /Starter files/ASP.NET Core Minimal API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "74fd1f8b-92bb-42b3-bc4b-7504604f66be", 4 | "name": "ASP.NET Core Minimal API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "805617" 7 | }, 8 | "item": [ 9 | { 10 | "name": "GET WeatherForecast", 11 | "request": { 12 | "method": "GET", 13 | "header": [], 14 | "url": { 15 | "raw": "https://localhost:{{portNumber}}/weatherforecast", 16 | "protocol": "https", 17 | "host": [ 18 | "localhost" 19 | ], 20 | "port": "{{portNumber}}", 21 | "path": [ 22 | "weatherforecast" 23 | ] 24 | } 25 | }, 26 | "response": [] 27 | }, 28 | { 29 | "name": "GET Dishes", 30 | "request": { 31 | "method": "GET", 32 | "header": [ 33 | { 34 | "key": "", 35 | "value": "", 36 | "type": "text", 37 | "disabled": true 38 | } 39 | ], 40 | "url": { 41 | "raw": "https://localhost:{{portNumber}}/dishes", 42 | "protocol": "https", 43 | "host": [ 44 | "localhost" 45 | ], 46 | "port": "{{portNumber}}", 47 | "path": [ 48 | "dishes" 49 | ] 50 | } 51 | }, 52 | "response": [] 53 | }, 54 | { 55 | "name": "GET Dish", 56 | "request": { 57 | "method": "GET", 58 | "header": [], 59 | "url": { 60 | "raw": "https://localhost:{{portNumber}}/dishes/fd630a57-2352-4731-b25c-db9cc7601b16", 61 | "protocol": "https", 62 | "host": [ 63 | "localhost" 64 | ], 65 | "port": "{{portNumber}}", 66 | "path": [ 67 | "dishes", 68 | "fd630a57-2352-4731-b25c-db9cc7601b16" 69 | ] 70 | } 71 | }, 72 | "response": [] 73 | }, 74 | { 75 | "name": "GET Ingredients", 76 | "request": { 77 | "method": "GET", 78 | "header": [], 79 | "url": { 80 | "raw": "https://localhost:{{portNumber}}/dishes/fd630a57-2352-4731-b25c-db9cc7601b16/ingredients", 81 | "protocol": "https", 82 | "host": [ 83 | "localhost" 84 | ], 85 | "port": "{{portNumber}}", 86 | "path": [ 87 | "dishes", 88 | "fd630a57-2352-4731-b25c-db9cc7601b16", 89 | "ingredients" 90 | ] 91 | } 92 | }, 93 | "response": [] 94 | }, 95 | { 96 | "name": "GET Dish (by name)", 97 | "request": { 98 | "method": "GET", 99 | "header": [], 100 | "url": { 101 | "raw": "https://localhost:{{portNumber}}/dishes/Rendang", 102 | "protocol": "https", 103 | "host": [ 104 | "localhost" 105 | ], 106 | "port": "{{portNumber}}", 107 | "path": [ 108 | "dishes", 109 | "Rendang" 110 | ] 111 | } 112 | }, 113 | "response": [] 114 | }, 115 | { 116 | "name": "GET Dishes (filtered)", 117 | "request": { 118 | "method": "GET", 119 | "header": [], 120 | "url": { 121 | "raw": "https://localhost:{{portNumber}}/dishes?name=Mussels", 122 | "protocol": "https", 123 | "host": [ 124 | "localhost" 125 | ], 126 | "port": "{{portNumber}}", 127 | "path": [ 128 | "dishes" 129 | ], 130 | "query": [ 131 | { 132 | "key": "name", 133 | "value": "Mussels" 134 | } 135 | ] 136 | } 137 | }, 138 | "response": [] 139 | }, 140 | { 141 | "name": "GET Dish (not found)", 142 | "request": { 143 | "method": "GET", 144 | "header": [], 145 | "url": { 146 | "raw": "https://localhost:{{portNumber}}/dishes/b539b05a-4c49-44b4-98fc-3b072d5fd3f8", 147 | "protocol": "https", 148 | "host": [ 149 | "localhost" 150 | ], 151 | "port": "{{portNumber}}", 152 | "path": [ 153 | "dishes", 154 | "b539b05a-4c49-44b4-98fc-3b072d5fd3f8" 155 | ] 156 | } 157 | }, 158 | "response": [] 159 | }, 160 | { 161 | "name": "POST Dish", 162 | "request": { 163 | "method": "POST", 164 | "header": [ 165 | { 166 | "key": "Content-Type", 167 | "value": "application/json", 168 | "type": "text" 169 | } 170 | ], 171 | "body": { 172 | "mode": "raw", 173 | "raw": "{\r\n \"name\": \"Pizza Hawaii\"\r\n}" 174 | }, 175 | "url": { 176 | "raw": "https://localhost:{{portNumber}}/dishes", 177 | "protocol": "https", 178 | "host": [ 179 | "localhost" 180 | ], 181 | "port": "{{portNumber}}", 182 | "path": [ 183 | "dishes" 184 | ] 185 | } 186 | }, 187 | "response": [] 188 | }, 189 | { 190 | "name": "PUT Dish", 191 | "request": { 192 | "method": "PUT", 193 | "header": [ 194 | { 195 | "key": "Content-Type", 196 | "value": "application/json", 197 | "type": "text" 198 | } 199 | ], 200 | "body": { 201 | "mode": "raw", 202 | "raw": "{\r\n \"name\": \"Updated Dish\"\r\n}" 203 | }, 204 | "url": { 205 | "raw": "https://localhost:{{portNumber}}/dishes/fd630a57-2352-4731-b25c-db9cc7601b16", 206 | "protocol": "https", 207 | "host": [ 208 | "localhost" 209 | ], 210 | "port": "{{portNumber}}", 211 | "path": [ 212 | "dishes", 213 | "fd630a57-2352-4731-b25c-db9cc7601b16" 214 | ] 215 | } 216 | }, 217 | "response": [] 218 | }, 219 | { 220 | "name": "DELETE Dish", 221 | "request": { 222 | "method": "DELETE", 223 | "header": [ 224 | { 225 | "key": "", 226 | "value": "", 227 | "type": "text", 228 | "disabled": true 229 | } 230 | ], 231 | "body": { 232 | "mode": "raw", 233 | "raw": "" 234 | }, 235 | "url": { 236 | "raw": "https://localhost:{{portNumber}}/dishes/fd630a57-2352-4731-b25c-db9cc7601b16", 237 | "protocol": "https", 238 | "host": [ 239 | "localhost" 240 | ], 241 | "port": "{{portNumber}}", 242 | "path": [ 243 | "dishes", 244 | "fd630a57-2352-4731-b25c-db9cc7601b16" 245 | ] 246 | } 247 | }, 248 | "response": [] 249 | }, 250 | { 251 | "name": "POST Ingredient", 252 | "request": { 253 | "method": "POST", 254 | "header": [ 255 | { 256 | "key": "Content-Type", 257 | "value": "application/json", 258 | "type": "text" 259 | } 260 | ], 261 | "body": { 262 | "mode": "raw", 263 | "raw": "{\r\n \"name\": \"Pizza Hawaii\"\r\n}" 264 | }, 265 | "url": { 266 | "raw": "https://localhost:{{portNumber}}/dishes", 267 | "protocol": "https", 268 | "host": [ 269 | "localhost" 270 | ], 271 | "port": "{{portNumber}}", 272 | "path": [ 273 | "dishes" 274 | ] 275 | } 276 | }, 277 | "response": [] 278 | }, 279 | { 280 | "name": "DELETE Dish (Flemish Stew)", 281 | "request": { 282 | "method": "DELETE", 283 | "header": [ 284 | { 285 | "key": "", 286 | "value": "", 287 | "type": "text", 288 | "disabled": true 289 | } 290 | ], 291 | "body": { 292 | "mode": "raw", 293 | "raw": "" 294 | }, 295 | "url": { 296 | "raw": "https://localhost:{{portNumber}}/dishes/eacc5169-b2a7-41ad-92c3-dbb1a5e7af06", 297 | "protocol": "https", 298 | "host": [ 299 | "localhost" 300 | ], 301 | "port": "{{portNumber}}", 302 | "path": [ 303 | "dishes", 304 | "eacc5169-b2a7-41ad-92c3-dbb1a5e7af06" 305 | ] 306 | } 307 | }, 308 | "response": [] 309 | }, 310 | { 311 | "name": "DELETE Dish (not found)", 312 | "request": { 313 | "method": "DELETE", 314 | "header": [ 315 | { 316 | "key": "", 317 | "value": "", 318 | "type": "text", 319 | "disabled": true 320 | } 321 | ], 322 | "body": { 323 | "mode": "raw", 324 | "raw": "" 325 | }, 326 | "url": { 327 | "raw": "https://localhost:{{portNumber}}/dishes/e6c79d8c-8ab1-4b88-ab17-b008e21e053e", 328 | "protocol": "https", 329 | "host": [ 330 | "localhost" 331 | ], 332 | "port": "{{portNumber}}", 333 | "path": [ 334 | "dishes", 335 | "e6c79d8c-8ab1-4b88-ab17-b008e21e053e" 336 | ] 337 | } 338 | }, 339 | "response": [] 340 | }, 341 | { 342 | "name": "POST Dish (invalid - name too short))", 343 | "request": { 344 | "method": "POST", 345 | "header": [ 346 | { 347 | "key": "Content-Type", 348 | "value": "application/json", 349 | "type": "text" 350 | } 351 | ], 352 | "body": { 353 | "mode": "raw", 354 | "raw": "{\r\n \"name\": \"A\"\r\n}" 355 | }, 356 | "url": { 357 | "raw": "https://localhost:{{portNumber}}/dishes", 358 | "protocol": "https", 359 | "host": [ 360 | "localhost" 361 | ], 362 | "port": "{{portNumber}}", 363 | "path": [ 364 | "dishes" 365 | ] 366 | } 367 | }, 368 | "response": [] 369 | }, 370 | { 371 | "name": "POST Dish (invalid - name null)", 372 | "request": { 373 | "method": "POST", 374 | "header": [ 375 | { 376 | "key": "Content-Type", 377 | "value": "application/json", 378 | "type": "text" 379 | } 380 | ], 381 | "body": { 382 | "mode": "raw", 383 | "raw": "{\r\n \"name\": null\r\n}" 384 | }, 385 | "url": { 386 | "raw": "https://localhost:{{portNumber}}/dishes", 387 | "protocol": "https", 388 | "host": [ 389 | "localhost" 390 | ], 391 | "port": "{{portNumber}}", 392 | "path": [ 393 | "dishes" 394 | ] 395 | } 396 | }, 397 | "response": [] 398 | } 399 | ] 400 | } -------------------------------------------------------------------------------- /Starter files/DbContexts/DishesDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.Hosting; 3 | using DishesAPI.Entities; 4 | using System.IO; 5 | 6 | namespace DishesAPI.DbContexts; 7 | 8 | public class DishesDbContext : DbContext 9 | { 10 | public DbSet Dishes { get; set; } = null!; 11 | public DbSet Ingredients { get; set; } = null!; 12 | 13 | 14 | public DishesDbContext(DbContextOptions options) 15 | : base(options) 16 | { 17 | } 18 | 19 | protected override void OnModelCreating(ModelBuilder modelBuilder) 20 | { 21 | _ = modelBuilder.Entity().HasData( 22 | new(Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35"), "Beef"), 23 | new(Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96"), "Onion"), 24 | new(Guid.Parse("c19099ed-94db-44ba-885b-0ad7205d5e40"), "Dark beer"), 25 | new(Guid.Parse("0c4dc798-b38b-4a1c-905c-a9e76dbef17b"), "Brown piece of bread"), 26 | new(Guid.Parse("937b1ba1-7969-4324-9ab5-afb0e4d875e6"), "Mustard"), 27 | new(Guid.Parse("7a2fbc72-bb33-49de-bd23-c78fceb367fc"), "Chicory"), 28 | new(Guid.Parse("b5f336e2-c226-4389-aac3-2499325a3de9"), "Mayo"), 29 | new(Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe"), "Various spices"), 30 | new(Guid.Parse("aab31c70-57ce-4b6d-a66c-9c1b094e915d"), "Mussels"), 31 | new(Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f"), "Celery"), 32 | new(Guid.Parse("8d5a1b40-6677-4545-b6e8-5ba93efda0a1"), "French fries"), 33 | new(Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4"), "Tomato"), 34 | new(Guid.Parse("f350e1a0-38de-42fe-ada5-ae436378ee5b"), "Tomato paste"), 35 | new(Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d"), "Bay leave"), 36 | new(Guid.Parse("b617df23-3d91-40e1-99aa-b07d264aa937"), "Carrot"), 37 | new(Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56"), "Garlic"), 38 | new(Guid.Parse("ecd396c3-4403-4fbf-83ca-94a8e9d859b3"), "Red wine"), 39 | new(Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671"), "Coconut milk"), 40 | new(Guid.Parse("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7"), "Ginger"), 41 | new(Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237"), "Chili pepper"), 42 | new(Guid.Parse("e0017fe1-773f-4a59-9730-9489833c6e8e"), "Tamarind paste"), 43 | new(Guid.Parse("c9b46f9c-d6ce-42c3-8736-2cddbbadee10"), "Firm fish"), 44 | new(Guid.Parse("a07cde83-3127-45da-bbd5-04a7c8d13aa4"), "Ginger garlic paste"), 45 | new(Guid.Parse("ebe94d5d-2ad8-4886-b246-05a1fad83d1c"), "Garam masala")); 46 | 47 | _ = modelBuilder.Entity().HasData( 48 | new(Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), 49 | "Flemish Beef stew with chicory" ), 50 | new(Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), 51 | "Mussels with french fries" ), 52 | new(Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), 53 | "Ragu alla bolognaise"), 54 | new(Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), 55 | "Rendang"), 56 | new(Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), 57 | "Fish Masala")); 58 | 59 | _ = modelBuilder 60 | .Entity() 61 | .HasMany(d => d.Ingredients) 62 | .WithMany(i => i.Dishes) 63 | .UsingEntity(e => e.HasData( 64 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 65 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 66 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("c19099ed-94db-44ba-885b-0ad7205d5e40") }, 67 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("0c4dc798-b38b-4a1c-905c-a9e76dbef17b") }, 68 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("937b1ba1-7969-4324-9ab5-afb0e4d875e6") }, 69 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("7a2fbc72-bb33-49de-bd23-c78fceb367fc") }, 70 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("b5f336e2-c226-4389-aac3-2499325a3de9") }, 71 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 72 | new { DishesId = Guid.Parse("eacc5169-b2a7-41ad-92c3-dbb1a5e7af06"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 73 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("aab31c70-57ce-4b6d-a66c-9c1b094e915d") }, 74 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") }, 75 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("8d5a1b40-6677-4545-b6e8-5ba93efda0a1") }, 76 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 77 | new { DishesId = Guid.Parse("fe462ec7-b30c-4987-8a8e-5f7dbd8e0cfa"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 78 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 79 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4") }, 80 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("f350e1a0-38de-42fe-ada5-ae436378ee5b") }, 81 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 82 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("fef8b722-664d-403f-ae3c-05f8ed7d7a1f") }, 83 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("b617df23-3d91-40e1-99aa-b07d264aa937") }, 84 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 85 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 86 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("ecd396c3-4403-4fbf-83ca-94a8e9d859b3") }, 87 | new { DishesId = Guid.Parse("b512d7cf-b331-4b54-8dae-d1228d128e8d"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 88 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("d28888e9-2ba9-473a-a40f-e38cb54f9b35") }, 89 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671") }, 90 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("b8b9a6ae-9bcc-4fb3-b883-5974e04eda56") }, 91 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("3bd3f0a1-87d3-4b85-94fa-ba92bd1874e7") }, 92 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237") }, 93 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 94 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("e0017fe1-773f-4a59-9730-9489833c6e8e") }, 95 | new { DishesId = Guid.Parse("fd630a57-2352-4731-b25c-db9cc7601b16"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") }, 96 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c9b46f9c-d6ce-42c3-8736-2cddbbadee10") }, 97 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("a07cde83-3127-45da-bbd5-04a7c8d13aa4") }, 98 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("ebe94d5d-2ad8-4886-b246-05a1fad83d1c") }, 99 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("da2fd609-d754-4feb-8acd-c4f9ff13ba96") }, 100 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("40563e5b-e538-4084-9587-3df74fae21d4") }, 101 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c2c75b40-2453-416e-a7ed-3505b121d671") }, 102 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("d5cad9a4-144e-4a3d-858d-9840792fa65d") }, 103 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("047ab5cc-d041-486e-9d22-a0860fb13237") }, 104 | new { DishesId = Guid.Parse("98929bd4-f099-41eb-a994-f1918b724b5a"), IngredientsId = Guid.Parse("c22bec27-a880-4f2a-b380-12dcd99c61fe") } 105 | )); 106 | 107 | base.OnModelCreating(modelBuilder); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Starter files/Entities/Dish.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace DishesAPI.Entities; 6 | 7 | public class Dish 8 | { 9 | [Key] 10 | public Guid Id { get; set; } 11 | 12 | // "required" modifier: compiler guarantees Name is initialized when the Dish is instantiated. 13 | // see: https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types 14 | // & https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#required-members 15 | [Required] 16 | [MaxLength(200)] 17 | public required string Name { get; set; } 18 | 19 | public ICollection Ingredients { get; set; } = new List(); 20 | 21 | public Dish() 22 | { 23 | } 24 | 25 | [SetsRequiredMembers] 26 | public Dish(Guid id, string name) 27 | { 28 | Id = id; 29 | Name = name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Starter files/Entities/Ingredient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query.Internal; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace DishesAPI.Entities; 6 | 7 | public class Ingredient 8 | { 9 | [Key] 10 | public Guid Id { get; set; } 11 | 12 | [Required] 13 | [MaxLength(200)] 14 | public required string Name { get; set; } 15 | 16 | public ICollection Dishes { get; set; } = new List(); 17 | 18 | public Ingredient() 19 | { } 20 | 21 | [SetsRequiredMembers] 22 | public Ingredient(Guid id, string name) 23 | { 24 | Id = id; 25 | Name = name; 26 | } 27 | } 28 | --------------------------------------------------------------------------------