├── .gitattributes ├── .gitignore ├── 1 - Express Designs Better └── Express Designs Better.pptx ├── 2 - Tuples and Patterns ├── Lab │ ├── Completed │ │ ├── TuplesAndPatterns.sln │ │ └── TuplesAndPatterns │ │ │ ├── ConnectedSensor.cs │ │ │ ├── Program.cs │ │ │ └── TuplesAndPatterns.csproj │ └── Started │ │ ├── TuplesAndPatterns.sln │ │ └── TuplesAndPatterns │ │ ├── ConnectedSensor.cs │ │ ├── Program.cs │ │ └── TuplesAndPatterns.csproj ├── README.md └── Tuples And Patterns.pptx ├── 3 - Exceptions and Errors ├── Exceptions and Errors.pptx ├── Lab │ ├── Completed │ │ ├── ExceptionsDemo.sln │ │ └── ExceptionsDemo │ │ │ ├── ExceptionsDemo.csproj │ │ │ └── Program.cs │ └── Started │ │ ├── ExceptionsDemo.sln │ │ └── ExceptionsDemo │ │ ├── ExceptionsDemo.csproj │ │ └── Program.cs └── README.md ├── 4 - Memory And Performance ├── Lab │ └── Benchmark │ │ ├── Benchmark.sln │ │ └── Benchmark │ │ ├── Benchmark.csproj │ │ ├── BenchmarkDotNet.Artifacts │ │ └── results │ │ │ ├── Benchmark.Benchmarks-report-github.md │ │ │ ├── Benchmark.Benchmarks-report.csv │ │ │ └── Benchmark.Benchmarks-report.html │ │ ├── LargeImmutableStruct.cs │ │ ├── LargeMutableStruct.cs │ │ └── Program.cs ├── Memory and Performance.pptx └── README.md ├── 5 - One Abstraction ├── Labs │ ├── Completed │ │ └── AsyncBreakfast │ │ │ ├── AsyncBreakfast.sln │ │ │ └── AsyncBreakfast │ │ │ ├── AsyncBreakfast.csproj │ │ │ ├── Bacon.cs │ │ │ ├── Coffee.cs │ │ │ ├── Egg.cs │ │ │ ├── Juice.cs │ │ │ ├── Program.cs │ │ │ └── Toast.cs │ ├── First Checkpoint │ │ └── AsyncBreakfast │ │ │ ├── AsyncBreakfast.sln │ │ │ └── AsyncBreakfast │ │ │ ├── AsyncBreakfast.csproj │ │ │ ├── Bacon.cs │ │ │ ├── Coffee.cs │ │ │ ├── Egg.cs │ │ │ ├── Juice.cs │ │ │ ├── Program.cs │ │ │ └── Toast.cs │ └── Started │ │ └── AsyncBreakfast │ │ ├── AsyncBreakfast.sln │ │ └── AsyncBreakfast │ │ ├── AsyncBreakfast.csproj │ │ ├── Bacon.cs │ │ ├── Coffee.cs │ │ ├── Egg.cs │ │ ├── Juice.cs │ │ ├── Program.cs │ │ └── Toast.cs ├── One Abstraction.pptx └── README.md ├── 6 - Task Composition ├── Lab │ ├── Completed │ │ └── IssuePRreport │ │ │ ├── IssuePRreport.sln │ │ │ └── IssuePRreport │ │ │ ├── IssuePRreport.csproj │ │ │ └── Program.cs │ └── Started │ │ └── IssuePRreport │ │ ├── IssuePRreport.sln │ │ └── IssuePRreport │ │ ├── IssuePRreport.csproj │ │ └── Program.cs ├── README.md └── Task Composition.pptx ├── 7 - Advanced Task Composition ├── Advanced Task Composition.pptx ├── Lab │ ├── Completed │ │ └── IssuePRreport │ │ │ ├── IssuePRreport.sln │ │ │ └── IssuePRreport │ │ │ ├── IssuePRreport.csproj │ │ │ └── Program.cs │ └── Started │ │ └── IssuePRreport │ │ ├── IssuePRreport.sln │ │ └── IssuePRreport │ │ ├── IssuePRreport.csproj │ │ └── Program.cs └── README.md ├── 8 - State Machine Future └── State Machine Future.pptx ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | *.ppt* filter=lfs diff=lfs merge=lfs -text 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xamarinstudio,visualstudio,visualstudiocode,xcode,android,macos,csharp,f#,fastlane,java,jetbrains,linux,monodevelop,objective-c,swift,sublimetext,unity 3 | 4 | ### fastlane ### 5 | # fastlane - A streamlined workflow tool for Cocoa deployment 6 | 7 | # fastlane specific 8 | fastlane/report.xml 9 | 10 | # deliver temporary files 11 | fastlane/Preview.html 12 | 13 | # snapshot generated screenshots 14 | fastlane/screenshots/**/*.png 15 | fastlane/screenshots/screenshots.html 16 | 17 | # scan temporary files 18 | fastlane/test_output 19 | 20 | 21 | ### XamarinStudio ### 22 | bin/ 23 | obj/ 24 | *.userprefs 25 | 26 | 27 | ### VisualStudioCode ### 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | 34 | 35 | ### Xcode ### 36 | # Xcode 37 | # 38 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 39 | 40 | ## Build generated 41 | build/ 42 | DerivedData/ 43 | 44 | ## Various settings 45 | *.pbxuser 46 | !default.pbxuser 47 | *.mode1v3 48 | !default.mode1v3 49 | *.mode2v3 50 | !default.mode2v3 51 | *.perspectivev3 52 | !default.perspectivev3 53 | xcuserdata/ 54 | 55 | ## Other 56 | *.moved-aside 57 | *.xccheckout 58 | *.xcscmblueprint 59 | 60 | 61 | ### Android ### 62 | # Built application files 63 | *.apk 64 | *.ap_ 65 | 66 | # Files for the ART/Dalvik VM 67 | *.dex 68 | 69 | # Java class files 70 | *.class 71 | 72 | # Generated files 73 | gen/ 74 | out/ 75 | Resource.designer.cs 76 | 77 | # Gradle files 78 | .gradle/ 79 | 80 | # Local configuration file (sdk path, etc) 81 | local.properties 82 | 83 | # Proguard folder generated by Eclipse 84 | proguard/ 85 | 86 | # Log Files 87 | *.log 88 | 89 | # Android Studio Navigation editor temp files 90 | .navigation/ 91 | 92 | # Android Studio captures folder 93 | captures/ 94 | 95 | # Intellij 96 | *.iml 97 | .idea/workspace.xml 98 | .idea/tasks.xml 99 | .idea/libraries 100 | 101 | # Keystore files 102 | *.jks 103 | 104 | # External native build folder generated in Android Studio 2.2 and later 105 | .externalNativeBuild 106 | 107 | ### Android Patch ### 108 | gen-external-apklibs 109 | 110 | 111 | ### macOS ### 112 | *.DS_Store 113 | .AppleDouble 114 | .LSOverride 115 | 116 | # Icon must end with two \r 117 | Icon 118 | # Thumbnails 119 | ._* 120 | # Files that might appear in the root of a volume 121 | .DocumentRevisions-V100 122 | .fseventsd 123 | .Spotlight-V100 124 | .TemporaryItems 125 | .Trashes 126 | .VolumeIcon.icns 127 | .com.apple.timemachine.donotpresent 128 | # Directories potentially created on remote AFP share 129 | .AppleDB 130 | .AppleDesktop 131 | Network Trash Folder 132 | Temporary Items 133 | .apdisk 134 | 135 | 136 | ### Csharp ### 137 | ## Ignore Visual Studio temporary files, build results, and 138 | ## files generated by popular Visual Studio add-ons. 139 | ## 140 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 141 | 142 | # User-specific files 143 | *.suo 144 | *.user 145 | *.userosscache 146 | *.sln.docstates 147 | *.vcxproj.filters 148 | 149 | # User-specific files (MonoDevelop/Xamarin Studio) 150 | 151 | # Build results 152 | [Dd]ebug/ 153 | [Dd]ebugPublic/ 154 | [Rr]elease/ 155 | [Rr]eleases/ 156 | x64/ 157 | x86/ 158 | bld/ 159 | [Bb]in/ 160 | [Oo]bj/ 161 | [Ll]og/ 162 | 163 | # Visual Studio 2015 cache/options directory 164 | .vs/ 165 | # Uncomment if you have tasks that create the project's static files in wwwroot 166 | #wwwroot/ 167 | 168 | # MSTest test Results 169 | [Tt]est[Rr]esult*/ 170 | [Bb]uild[Ll]og.* 171 | 172 | # NUNIT 173 | *.VisualState.xml 174 | TestResult.xml 175 | 176 | # Build Results of an ATL Project 177 | [Dd]ebugPS/ 178 | [Rr]eleasePS/ 179 | dlldata.c 180 | 181 | # .NET Core 182 | project.lock.json 183 | project.fragment.lock.json 184 | artifacts/ 185 | **/Properties/launchSettings.json 186 | 187 | *_i.c 188 | *_p.c 189 | *_i.h 190 | *.ilk 191 | *.meta 192 | *.obj 193 | *.pch 194 | *.pdb 195 | *.pgc 196 | *.pgd 197 | *.rsp 198 | *.sbr 199 | *.tlb 200 | *.tli 201 | *.tlh 202 | *.tmp 203 | *.tmp_proj 204 | *.vspscc 205 | *.vssscc 206 | .builds 207 | *.pidb 208 | *.svclog 209 | *.scc 210 | 211 | # Chutzpah Test files 212 | _Chutzpah* 213 | 214 | # Visual C++ cache files 215 | ipch/ 216 | *.aps 217 | *.ncb 218 | *.opendb 219 | *.opensdf 220 | *.sdf 221 | *.cachefile 222 | *.VC.db 223 | *.VC.VC.opendb 224 | 225 | # Visual Studio profiler 226 | *.psess 227 | *.vsp 228 | *.vspx 229 | *.sap 230 | 231 | # TFS 2012 Local Workspace 232 | $tf/ 233 | 234 | # Guidance Automation Toolkit 235 | *.gpState 236 | 237 | # ReSharper is a .NET coding add-in 238 | _ReSharper*/ 239 | *.[Rr]e[Ss]harper 240 | *.DotSettings.user 241 | 242 | # JustCode is a .NET coding add-in 243 | .JustCode 244 | 245 | # TeamCity is a build add-in 246 | _TeamCity* 247 | 248 | # DotCover is a Code Coverage Tool 249 | *.dotCover 250 | 251 | # Visual Studio code coverage results 252 | *.coverage 253 | *.coveragexml 254 | 255 | # NCrunch 256 | _NCrunch_* 257 | .*crunch*.local.xml 258 | nCrunchTemp_* 259 | 260 | # MightyMoose 261 | *.mm.* 262 | AutoTest.Net/ 263 | 264 | # Web workbench (sass) 265 | .sass-cache/ 266 | 267 | # Installshield output folder 268 | [Ee]xpress/ 269 | 270 | # DocProject is a documentation generator add-in 271 | DocProject/buildhelp/ 272 | DocProject/Help/*.HxT 273 | DocProject/Help/*.HxC 274 | DocProject/Help/*.hhc 275 | DocProject/Help/*.hhk 276 | DocProject/Help/*.hhp 277 | DocProject/Help/Html2 278 | DocProject/Help/html 279 | 280 | # Click-Once directory 281 | publish/ 282 | 283 | # Publish Web Output 284 | *.[Pp]ublish.xml 285 | *.azurePubxml 286 | # TODO: Comment the next line if you want to checkin your web deploy settings 287 | # but database connection strings (with potential passwords) will be unencrypted 288 | *.pubxml 289 | *.publishproj 290 | 291 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 292 | # checkin your Azure Web App publish settings, but sensitive information contained 293 | # in these scripts will be unencrypted 294 | PublishScripts/ 295 | 296 | # NuGet Packages 297 | *.nupkg 298 | # The packages folder can be ignored because of Package Restore 299 | **/packages/* 300 | # except build/, which is used as an MSBuild target. 301 | !**/packages/build/ 302 | # Uncomment if necessary however generally it will be regenerated when needed 303 | #!**/packages/repositories.config 304 | # NuGet v3's project.json files produces more ignoreable files 305 | *.nuget.props 306 | *.nuget.targets 307 | 308 | # Microsoft Azure Build Output 309 | csx/ 310 | *.build.csdef 311 | 312 | # Microsoft Azure Emulator 313 | ecf/ 314 | rcf/ 315 | 316 | # Windows Store app package directories and files 317 | AppPackages/ 318 | BundleArtifacts/ 319 | _pkginfo.txt 320 | 321 | # Visual Studio cache files 322 | # files ending in .cache can be ignored 323 | *.[Cc]ache 324 | # but keep track of directories ending in .cache 325 | !*.[Cc]ache/ 326 | 327 | # Others 328 | ClientBin/ 329 | ~$* 330 | *~ 331 | *.dbmdl 332 | *.dbproj.schemaview 333 | *.jfm 334 | *.pfx 335 | *.publishsettings 336 | node_modules/ 337 | orleans.codegen.cs 338 | 339 | # Since there are multiple workflows, uncomment next line to ignore bower_components 340 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 341 | #bower_components/ 342 | 343 | # RIA/Silverlight projects 344 | Generated_Code/ 345 | 346 | # Backup & report files from converting an old project file 347 | # to a newer Visual Studio version. Backup files are not needed, 348 | # because we have git ;-) 349 | _UpgradeReport_Files/ 350 | Backup*/ 351 | UpgradeLog*.XML 352 | UpgradeLog*.htm 353 | 354 | # SQL Server files 355 | *.mdf 356 | *.ldf 357 | 358 | # Business Intelligence projects 359 | *.rdl.data 360 | *.bim.layout 361 | *.bim_*.settings 362 | 363 | # Microsoft Fakes 364 | FakesAssemblies/ 365 | 366 | # GhostDoc plugin setting file 367 | *.GhostDoc.xml 368 | 369 | # Node.js Tools for Visual Studio 370 | .ntvs_analysis.dat 371 | 372 | # Visual Studio 6 build log 373 | *.plg 374 | 375 | # Visual Studio 6 workspace options file 376 | *.opt 377 | 378 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 379 | *.vbw 380 | 381 | # Visual Studio LightSwitch build output 382 | **/*.HTMLClient/GeneratedArtifacts 383 | **/*.DesktopClient/GeneratedArtifacts 384 | **/*.DesktopClient/ModelManifest.xml 385 | **/*.Server/GeneratedArtifacts 386 | **/*.Server/ModelManifest.xml 387 | _Pvt_Extensions 388 | 389 | # Paket dependency manager 390 | .paket/paket.exe 391 | paket-files/ 392 | 393 | # FAKE - F# Make 394 | .fake/ 395 | 396 | # JetBrains Rider 397 | .idea/ 398 | *.sln.iml 399 | 400 | # CodeRush 401 | .cr/ 402 | 403 | # Python Tools for Visual Studio (PTVS) 404 | __pycache__/ 405 | *.pyc 406 | 407 | # Cake - Uncomment if you are using it 408 | # tools/ 409 | 410 | 411 | ### F# ### 412 | lib/debug 413 | lib/release 414 | Debug 415 | obj 416 | bin 417 | *.exe 418 | !.paket/paket.bootstrapper.exe 419 | 420 | 421 | ### SublimeText ### 422 | # cache files for sublime text 423 | *.tmlanguage.cache 424 | *.tmPreferences.cache 425 | *.stTheme.cache 426 | 427 | # workspace files are user-specific 428 | *.sublime-workspace 429 | 430 | # project files should be checked into the repository, unless a significant 431 | # proportion of contributors will probably not be using SublimeText 432 | # *.sublime-project 433 | 434 | # sftp configuration file 435 | sftp-config.json 436 | 437 | # Package control specific files 438 | Package Control.last-run 439 | Package Control.ca-list 440 | Package Control.ca-bundle 441 | Package Control.system-ca-bundle 442 | Package Control.cache/ 443 | Package Control.ca-certs/ 444 | bh_unicode_properties.cache 445 | 446 | # Sublime-github package stores a github token in this file 447 | # https://packagecontrol.io/packages/sublime-github 448 | GitHub.sublime-settings 449 | 450 | 451 | ### Swift ### 452 | # Xcode 453 | # 454 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 455 | 456 | ## Build generated 457 | 458 | ## Various settings 459 | 460 | ## Other 461 | *.xcuserstate 462 | 463 | ## Obj-C/Swift specific 464 | *.hmap 465 | *.ipa 466 | *.dSYM.zip 467 | *.dSYM 468 | 469 | ## Playgrounds 470 | timeline.xctimeline 471 | playground.xcworkspace 472 | 473 | # Swift Package Manager 474 | # 475 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 476 | # Packages/ 477 | .build/ 478 | 479 | # CocoaPods 480 | # 481 | # We recommend against adding the Pods directory to your .gitignore. However 482 | # you should judge for yourself, the pros and cons are mentioned at: 483 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 484 | # 485 | # Pods/ 486 | 487 | # Carthage 488 | # 489 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 490 | # Carthage/Checkouts 491 | 492 | Carthage/Build 493 | 494 | # fastlane 495 | # 496 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 497 | # screenshots whenever they are needed. 498 | # For more information about the recommended setup visit: 499 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 500 | 501 | fastlane/screenshots 502 | 503 | 504 | ### JetBrains ### 505 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 506 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 507 | 508 | # User-specific stuff: 509 | 510 | # Sensitive or high-churn files: 511 | .idea/dataSources/ 512 | .idea/dataSources.ids 513 | .idea/dataSources.xml 514 | .idea/dataSources.local.xml 515 | .idea/sqlDataSources.xml 516 | .idea/dynamic.xml 517 | .idea/uiDesigner.xml 518 | 519 | # Gradle: 520 | .idea/gradle.xml 521 | 522 | # Mongo Explorer plugin: 523 | .idea/mongoSettings.xml 524 | 525 | ## File-based project format: 526 | *.iws 527 | 528 | ## Plugin-specific files: 529 | 530 | # IntelliJ 531 | /out/ 532 | 533 | # mpeltonen/sbt-idea plugin 534 | .idea_modules/ 535 | 536 | # JIRA plugin 537 | atlassian-ide-plugin.xml 538 | 539 | # Crashlytics plugin (for Android Studio and IntelliJ) 540 | com_crashlytics_export_strings.xml 541 | crashlytics.properties 542 | crashlytics-build.properties 543 | fabric.properties 544 | 545 | ### JetBrains Patch ### 546 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 547 | 548 | # *.iml 549 | # modules.xml 550 | # .idea/misc.xml 551 | # *.ipr 552 | 553 | 554 | ### Linux ### 555 | 556 | # temporary files which can be created if a process still has a handle open of a deleted file 557 | .fuse_hidden* 558 | 559 | # KDE directory preferences 560 | .directory 561 | 562 | # Linux trash folder which might appear on any partition or disk 563 | .Trash-* 564 | 565 | # .nfs files are created when an open file is removed but is still being accessed 566 | .nfs* 567 | 568 | 569 | ### MonoDevelop ### 570 | #User Specific 571 | *.usertasks 572 | 573 | #Mono Project Files 574 | *.resources 575 | test-results/ 576 | 577 | 578 | ### Objective-C ### 579 | # Xcode 580 | # 581 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 582 | 583 | ## Build generated 584 | 585 | ## Various settings 586 | 587 | ## Other 588 | 589 | ## Obj-C/Swift specific 590 | 591 | # CocoaPods 592 | # 593 | # We recommend against adding the Pods directory to your .gitignore. However 594 | # you should judge for yourself, the pros and cons are mentioned at: 595 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 596 | # 597 | # Pods/ 598 | 599 | # Carthage 600 | # 601 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 602 | # Carthage/Checkouts 603 | 604 | 605 | # fastlane 606 | # 607 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 608 | # screenshots whenever they are needed. 609 | # For more information about the recommended setup visit: 610 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 611 | 612 | 613 | # Code Injection 614 | # 615 | # After new code Injection tools there's a generated folder /iOSInjectionProject 616 | # https://github.com/johnno1962/injectionforxcode 617 | 618 | iOSInjectionProject/ 619 | 620 | ### Objective-C Patch ### 621 | 622 | 623 | ### Unity ### 624 | /[Ll]ibrary/ 625 | /[Tt]emp/ 626 | /[Oo]bj/ 627 | /[Bb]uild/ 628 | /[Bb]uilds/ 629 | /Assets/AssetStoreTools* 630 | 631 | # Autogenerated VS/MD/Consulo solution and project files 632 | ExportedObj/ 633 | .consulo/*.csproj 634 | .consulo/*.unityproj 635 | .consulo/*.sln 636 | .consulo/*.booproj 637 | .consulo/*.svd 638 | 639 | 640 | # Unity3D generated meta files 641 | *.pidb.meta 642 | 643 | # Unity3D Generated File On Crash Reports 644 | sysinfo.txt 645 | 646 | # Builds 647 | *.unitypackage 648 | 649 | 650 | ### Java ### 651 | 652 | # BlueJ files 653 | *.ctxt 654 | 655 | # Mobile Tools for Java (J2ME) 656 | .mtj.tmp/ 657 | 658 | # Package Files # 659 | *.jar 660 | *.war 661 | *.ear 662 | 663 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 664 | hs_err_pid* 665 | 666 | 667 | ### VisualStudio ### 668 | ## Ignore Visual Studio temporary files, build results, and 669 | ## files generated by popular Visual Studio add-ons. 670 | ## 671 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 672 | 673 | # User-specific files 674 | 675 | # User-specific files (MonoDevelop/Xamarin Studio) 676 | 677 | # Build results 678 | 679 | # Visual Studio 2015 cache/options directory 680 | # Uncomment if you have tasks that create the project's static files in wwwroot 681 | #wwwroot/ 682 | 683 | # MSTest test Results 684 | 685 | # NUNIT 686 | 687 | # Build Results of an ATL Project 688 | 689 | # .NET Core 690 | 691 | 692 | # Chutzpah Test files 693 | 694 | # Visual C++ cache files 695 | 696 | # Visual Studio profiler 697 | 698 | # TFS 2012 Local Workspace 699 | 700 | # Guidance Automation Toolkit 701 | 702 | # ReSharper is a .NET coding add-in 703 | 704 | # JustCode is a .NET coding add-in 705 | 706 | # TeamCity is a build add-in 707 | 708 | # DotCover is a Code Coverage Tool 709 | 710 | # Visual Studio code coverage results 711 | 712 | # NCrunch 713 | 714 | # MightyMoose 715 | 716 | # Web workbench (sass) 717 | 718 | # Installshield output folder 719 | 720 | # DocProject is a documentation generator add-in 721 | 722 | # Click-Once directory 723 | 724 | # Publish Web Output 725 | # TODO: Comment the next line if you want to checkin your web deploy settings 726 | # but database connection strings (with potential passwords) will be unencrypted 727 | 728 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 729 | # checkin your Azure Web App publish settings, but sensitive information contained 730 | # in these scripts will be unencrypted 731 | 732 | # NuGet Packages 733 | # The packages folder can be ignored because of Package Restore 734 | # except build/, which is used as an MSBuild target. 735 | # Uncomment if necessary however generally it will be regenerated when needed 736 | #!**/packages/repositories.config 737 | # NuGet v3's project.json files produces more ignoreable files 738 | 739 | # Microsoft Azure Build Output 740 | 741 | # Microsoft Azure Emulator 742 | 743 | # Windows Store app package directories and files 744 | 745 | # Visual Studio cache files 746 | # files ending in .cache can be ignored 747 | # but keep track of directories ending in .cache 748 | 749 | # Others 750 | 751 | # Since there are multiple workflows, uncomment next line to ignore bower_components 752 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 753 | #bower_components/ 754 | 755 | # RIA/Silverlight projects 756 | 757 | # Backup & report files from converting an old project file 758 | # to a newer Visual Studio version. Backup files are not needed, 759 | # because we have git ;-) 760 | 761 | # SQL Server files 762 | 763 | # Business Intelligence projects 764 | 765 | # Microsoft Fakes 766 | 767 | # GhostDoc plugin setting file 768 | 769 | # Node.js Tools for Visual Studio 770 | 771 | # Visual Studio 6 build log 772 | 773 | # Visual Studio 6 workspace options file 774 | 775 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 776 | 777 | # Visual Studio LightSwitch build output 778 | 779 | # Paket dependency manager 780 | 781 | # FAKE - F# Make 782 | 783 | # JetBrains Rider 784 | 785 | # CodeRush 786 | 787 | # Python Tools for Visual Studio (PTVS) 788 | 789 | # Cake - Uncomment if you are using it 790 | # tools/ 791 | 792 | ### VisualStudio Patch ### 793 | 794 | # End of https://www.gitignore.io/api/xamarinstudio,visualstudio,visualstudiocode,xcode,android,macos,csharp,f#,fastlane,java,jetbrains,linux,monodevelop,objective-c,swift,sublimetext,unity 795 | -------------------------------------------------------------------------------- /1 - Express Designs Better/Express Designs Better.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f6c5d667a5fa80eba341e9a8ab6bb04a59f1ae26c9a1dc172920fcefb23cd928 3 | size 780089 4 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Completed/TuplesAndPatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TuplesAndPatterns", "TuplesAndPatterns\TuplesAndPatterns.csproj", "{289021F4-5132-4EE9-BCE2-CA9A836E51ED}" 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 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.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 = {332DD56E-5CDB-4CFE-B5F1-F8C09BF52523} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Completed/TuplesAndPatterns/ConnectedSensor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TuplesAndPatterns 6 | { 7 | public class ConnectedSensor 8 | { 9 | public IEnumerable ReadData(int numberDataPoints) 10 | { 11 | Random r = new Random((int)DateTime.Now.Ticks); 12 | 13 | foreach (var _ in Enumerable.Range(0, numberDataPoints)) 14 | yield return Math.Round((r.NextDouble() - 0.5) * 100, 2); // from -50 to 50 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Completed/TuplesAndPatterns/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json.Linq; 4 | using System.Linq; 5 | 6 | namespace TuplesAndPatterns 7 | { 8 | class Program 9 | { 10 | // Use a simple JSON object that cotnains points (x,y) or distance (dX, dY), 11 | // Use pattern matching to build the IEnumerable 12 | // It's simple to explain and simple to make it work. 13 | private const string jsonText = 14 | @"{ 15 | path: [ 16 | { 17 | X: 20, 18 | Y: 12 19 | }, 20 | { 21 | dx: -23, 22 | dy: -5 23 | }, 24 | { 25 | x: 5, 26 | y: 12 27 | }, 28 | { 29 | deltaX: 10, 30 | deltaY: 5 31 | } 32 | ] 33 | }"; 34 | 35 | static void Main(string[] args) 36 | { 37 | var points = GeneratePoints(50); 38 | 39 | var currentPoint = (X: 0.0, Y: 0.0); 40 | 41 | foreach (var point in points) 42 | { 43 | var xyDistance = (x: point.X - currentPoint.X, y: point.Y - currentPoint.Y); 44 | 45 | var distance = Math.Sqrt(xyDistance.x * xyDistance.x + xyDistance.y * xyDistance.y); 46 | 47 | Console.Write($"The distance from {currentPoint} to {point} is approximately {Math.Round(distance, 2)}"); 48 | 49 | if (ArePointsInSameQuadrant(currentPoint, point)) 50 | Console.WriteLine(" and they are in the same quadrant."); 51 | else 52 | Console.WriteLine(" and they are in different quadrants."); 53 | 54 | currentPoint = point; 55 | 56 | Console.WriteLine(); 57 | } 58 | 59 | points = GetParsedJSONPoints(jsonText); 60 | 61 | Console.WriteLine("Parsed JSON Output"); 62 | foreach (var point in points) 63 | Console.WriteLine(point); 64 | } 65 | 66 | static bool ArePointsInSameQuadrant((double X, double Y) left, (double X, double Y) right) => 67 | (Math.Sign(left.X), Math.Sign(left.Y)) == (Math.Sign(right.X), Math.Sign(right.Y)); 68 | 69 | static IEnumerable<(double X, double Y)> GetParsedJSONPoints(string json) 70 | { 71 | JObject data = JObject.Parse(json); 72 | 73 | JArray trip = (JArray)data["path"]; 74 | 75 | foreach (JObject obj in trip) 76 | { 77 | yield return ((double)obj.Values().First(), (double)obj.Values().Skip(1).First()); 78 | } 79 | } 80 | 81 | static IEnumerable<(double X, double Y)> GeneratePoints(int numberOfPointsToGenerate) 82 | { 83 | var generator = new ConnectedSensor(); 84 | 85 | var pointsEnumerator = generator.ReadData(numberOfPointsToGenerate * 2).GetEnumerator(); 86 | 87 | while (pointsEnumerator.MoveNext()) 88 | { 89 | var x = pointsEnumerator.Current; 90 | 91 | pointsEnumerator.MoveNext(); 92 | 93 | var y = pointsEnumerator.Current; 94 | 95 | yield return (x, y); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Completed/TuplesAndPatterns/TuplesAndPatterns.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | Latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Started/TuplesAndPatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TuplesAndPatterns", "TuplesAndPatterns\TuplesAndPatterns.csproj", "{289021F4-5132-4EE9-BCE2-CA9A836E51ED}" 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 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {289021F4-5132-4EE9-BCE2-CA9A836E51ED}.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 = {332DD56E-5CDB-4CFE-B5F1-F8C09BF52523} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Started/TuplesAndPatterns/ConnectedSensor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TuplesAndPatterns 6 | { 7 | public class ConnectedSensor 8 | { 9 | public IEnumerable ReadData(int numberDataPoints) 10 | { 11 | Random r = new Random((int)DateTime.Now.Ticks); 12 | 13 | foreach (var _ in Enumerable.Range(0, numberDataPoints)) 14 | yield return Math.Round((r.NextDouble() - 0.5) * 100, 2); // from -50 to 50 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Started/TuplesAndPatterns/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace TuplesAndPatterns 6 | { 7 | class Program 8 | { 9 | // Use a simple JSON object that contains points (x,y) or distance (dX, dY), 10 | // Use pattern matching to build the IEnumerable 11 | // It's simple to explain and simple to make it work. 12 | 13 | private const string jsonText = 14 | @"{ 15 | path: [ 16 | { 17 | X: 20, 18 | Y: 12 19 | }, 20 | { 21 | dx: -23, 22 | dy: -5 23 | }, 24 | { 25 | x: 5, 26 | y: 12 27 | }, 28 | { 29 | deltaX: 10, 30 | deltaY: 5 31 | } 32 | ] 33 | }"; 34 | 35 | static void Main(string[] args) 36 | { 37 | var points = GeneratePoints(50); 38 | 39 | var currentPoint = (X: 0.0, Y: 0.0); 40 | 41 | foreach (var point in points) 42 | { 43 | var xyDistance = (x: point.X - currentPoint.X, y: point.Y - currentPoint.Y); 44 | 45 | var distance = Math.Sqrt(xyDistance.x * xyDistance.x + xyDistance.y * xyDistance.y); 46 | 47 | Console.Write($"The distance from {currentPoint} to {point} is approximately {Math.Round(distance, 2)}"); 48 | 49 | if (ArePointsInSameQuadrant(currentPoint, point)) 50 | Console.WriteLine(" and they are in the same quadrant."); 51 | else 52 | Console.WriteLine(" and they are in different quadrants."); 53 | 54 | currentPoint = point; 55 | 56 | Console.WriteLine(); 57 | } 58 | 59 | points = GetParsedJSONPoints(jsonText); 60 | 61 | Console.WriteLine("Parsed JSON Output"); 62 | foreach (var point in points) 63 | Console.WriteLine(point); 64 | } 65 | 66 | static IEnumerable<(double X, double Y)> GetParsedJSONPoints(string json) 67 | { 68 | JObject data = JObject.Parse(json); 69 | 70 | JArray trip = (JArray)data["path"]; 71 | 72 | foreach (JObject obj in trip) 73 | { 74 | // TODO: Extend this to use pattern matching to return the next point. 75 | } 76 | 77 | throw new NotImplementedException("Delete this Exception once TODO is implemented"); 78 | } 79 | 80 | static bool ArePointsInSameQuadrant((double X, double Y) left, (double X, double Y) right) => 81 | (Math.Sign(left.X), Math.Sign(left.Y)) == (Math.Sign(right.X), Math.Sign(right.Y)); 82 | 83 | static IEnumerable<(double X, double Y)> GeneratePoints(int numberOfPointsToGenerate) 84 | { 85 | var generator = new ConnectedSensor(); 86 | 87 | var pointsEnumerator = generator.ReadData(numberOfPointsToGenerate * 2).GetEnumerator(); 88 | 89 | while (pointsEnumerator.MoveNext()) 90 | { 91 | var x = pointsEnumerator.Current; 92 | 93 | pointsEnumerator.MoveNext(); 94 | 95 | var y = pointsEnumerator.Current; 96 | 97 | yield return (x, y); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Lab/Started/TuplesAndPatterns/TuplesAndPatterns.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | Latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/README.md: -------------------------------------------------------------------------------- 1 | # Using Tuples 2 | 3 | ## Tuple Demo 4 | 5 | Show the generator class. Explain that this simulates and IoT sensor that returns 6 | pairs of numbers that should be formed into (x,y) bits. 7 | 8 | First part: create GetPoints() to return named tuples for X, Y 9 | 10 | Next, compote the distance between the points using tuples for dx, dy 11 | 12 | Add a SameQuadrant checking the sign of both points. 13 | 14 | ## Discussion: Rules for classes, structs, Tuples and anonymous types 15 | 16 | Ask the question: When should you pick tuples as your design choice? 17 | 18 | prediction that will lead to a bit fo a loss, without good answers. That's because tuples don't solve an *obvious* problem. 19 | 20 | Turn the discussion around: Ask when should you choose a class? When should you choose a struct? What features of classes or structs make them suitable for your designs? 21 | 22 | prediction: this gets to classes solving the problem of behavior + data. They include inheritance, interface implementation, member methods etc. 23 | Structs include member methods, can implement interfaces (yes, boxing), but no inheritance. 24 | 25 | Tuples have *none of those features*. They are a 'better' property bag. Let's compare that with our demo: So fare, the point has no behavior. Stated another way, that means you don't need member methods. They have no inheritance, they don't implement arbitrary interfaces. 26 | 27 | Consider features and patterns that would not be there if Tuples had existed since C# 1.0: 28 | - The try / out pattern 29 | - Others? (I don't know, but maybe we get good suggestions). 30 | 31 | ## Patterns bal 32 | 33 | Extend the point sample to read JSON that could be points, or could be (dx, dy) directions. 34 | 35 | Then, start computing the new location based on reading that kind of information. 36 | 37 | On the patterns: Point out that JSON data means it doesn't always have a structure. 38 | Deserialization could fail if properties are not prese3nt. By using *patterns* instead 39 | of a type, you continue to have the flexibility you need for extending the behavior without doing as much type plumbing when fields change (possibly not under your control.) 40 | 41 | Show a pattern where a switch looks at some data in an object to determine what to do. 42 | Explain that you could extend that so this code that retrieves points or deltas could generate a sequence of points. Have them do that as a lab. (Point out that the JSON is as well formed as we'd like. Sometimes it's "X", "Y". Sometimes it "x", "y", and so on. Patterns can manage that too.) 43 | 44 | Drive a discussion from this: 45 | 46 | When do the fields of a type change during development? 47 | 48 | Will patterns make it more resilient? Or is it lazy? What are the tradeoffs? 49 | -------------------------------------------------------------------------------- /2 - Tuples and Patterns/Tuples And Patterns.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9e57911672c020dbe6fb19a48b4ab4cdf7c21e40e962b99cdeb97e9653238859 3 | size 791082 4 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Exceptions and Errors.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4d17946a755aa3c952a1f924fa915f725215cfa933cbbefb3684404a947b1944 3 | size 781094 4 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Completed/ExceptionsDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExceptionsDemo", "ExceptionsDemo\ExceptionsDemo.csproj", "{A3C4387F-FF97-48A7-BC33-60A1C4119270}" 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 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.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 = {880FED0A-918C-40D8-843A-9D32AF21C69D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Completed/ExceptionsDemo/ExceptionsDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Completed/ExceptionsDemo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ExceptionsDemo 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | var theBank = new Till(50, 20, 10, 5); 10 | 11 | var expectedTotal = 50 * 1 + 20 * 5 + 10 * 10 + 5 * 20; 12 | 13 | theBank.LogTillStatus(); 14 | Console.WriteLine(theBank); 15 | Console.WriteLine($"Expected till value: {expectedTotal}"); 16 | 17 | int transactions = 500; 18 | var valueGenerator = new Random((int)DateTime.Now.Ticks); 19 | 20 | while (transactions-- > 0) 21 | { 22 | int itemCost = valueGenerator.Next(2, 50); 23 | 24 | int numOnes = itemCost % 2; 25 | int numFives = (itemCost % 10 > 7) ? 1 : 0; 26 | int numTens = (itemCost % 20 > 13) ? 1 : 0; 27 | int numTwenties = (itemCost < 20) ? 1 : 2; 28 | 29 | try 30 | { 31 | Console.WriteLine($"Customer making a £{itemCost} purchase"); 32 | Console.WriteLine($"\t Using {numTwenties} twenties"); 33 | Console.WriteLine($"\t Using {numTens} tenners"); 34 | Console.WriteLine($"\t Using {numFives} fivers"); 35 | Console.WriteLine($"\t Using {numOnes} one-pound coins"); 36 | 37 | theBank.MakeChange(itemCost, numTwenties, numTens, numFives, numOnes); 38 | 39 | expectedTotal += itemCost; 40 | } 41 | catch (InvalidOperationException e) 42 | { 43 | Console.WriteLine($"Could not make transaction: {e.Message}"); 44 | } 45 | 46 | Console.WriteLine(theBank); 47 | Console.WriteLine($"Expected till value: {expectedTotal}"); 48 | Console.WriteLine(); 49 | } 50 | } 51 | } 52 | 53 | 54 | 55 | public class Till 56 | { 57 | private int OneDollarBills; 58 | private int FiveDollarBills; 59 | private int TenDollarBills; 60 | private int TwentyDollarBills; 61 | 62 | public Till(int ones, int fives, int tens = 0, int twenties = 0) => 63 | (OneDollarBills, FiveDollarBills, TenDollarBills, TwentyDollarBills) = (ones, fives, tens, twenties); 64 | 65 | public void MakeChange(int cost, int twenties, int tens = 0, int fives = 0, int ones = 0) 66 | { 67 | var twentyDollarBillsInHand = TwentyDollarBills + twenties; 68 | var tenDollarBillsInHand = TenDollarBills + tens; 69 | var fiveDollarBillsInHand = FiveDollarBills + fives; 70 | var onesInHand = OneDollarBills + ones; 71 | 72 | int amountPaid = twenties * 20 + tens * 10 + fives * 5 + ones; 73 | int changeNeeded = amountPaid - cost; 74 | 75 | if (changeNeeded < 0) 76 | throw new InvalidOperationException("Not enough money provided"); 77 | 78 | Console.WriteLine("Cashier Returns:"); 79 | 80 | while ((changeNeeded > 19) && (TwentyDollarBills > 0)) 81 | { 82 | twentyDollarBillsInHand--; 83 | changeNeeded -= 20; 84 | Console.WriteLine("\t A twenty"); 85 | } 86 | 87 | while ((changeNeeded > 9) && (TenDollarBills > 0)) 88 | { 89 | tenDollarBillsInHand--; 90 | changeNeeded -= 10; 91 | Console.WriteLine("\t A tenner"); 92 | } 93 | 94 | while ((changeNeeded > 4) && (FiveDollarBills > 0)) 95 | { 96 | fiveDollarBillsInHand--; 97 | changeNeeded -= 5; 98 | Console.WriteLine("\t A fiver"); 99 | } 100 | 101 | while ((changeNeeded > 0) && (OneDollarBills > 0)) 102 | { 103 | onesInHand--; 104 | changeNeeded--; 105 | Console.WriteLine("\t A one"); 106 | } 107 | 108 | if (changeNeeded > 0) 109 | throw new InvalidOperationException("Can't make change. Do you have anything smaller?"); 110 | 111 | TwentyDollarBills = twentyDollarBillsInHand; 112 | TenDollarBills = tenDollarBillsInHand; 113 | FiveDollarBills = fiveDollarBillsInHand; 114 | OneDollarBills = onesInHand; 115 | } 116 | 117 | public void LogTillStatus() 118 | { 119 | Console.WriteLine("The till currently has:"); 120 | Console.WriteLine($"{TwentyDollarBills * 20} in twenties"); 121 | Console.WriteLine($"{TenDollarBills * 10} in tens"); 122 | Console.WriteLine($"{FiveDollarBills * 5} in fives"); 123 | Console.WriteLine($"{OneDollarBills} in ones"); 124 | Console.WriteLine(); 125 | } 126 | 127 | public override string ToString() => 128 | $"The till has {TwentyDollarBills * 20 + TenDollarBills * 10 + FiveDollarBills * 5 + OneDollarBills} dollars"; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Started/ExceptionsDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExceptionsDemo", "ExceptionsDemo\ExceptionsDemo.csproj", "{A3C4387F-FF97-48A7-BC33-60A1C4119270}" 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 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A3C4387F-FF97-48A7-BC33-60A1C4119270}.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 = {880FED0A-918C-40D8-843A-9D32AF21C69D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Started/ExceptionsDemo/ExceptionsDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/Lab/Started/ExceptionsDemo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ExceptionsDemo 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | var theBank = new Till(50, 20, 10, 5); 10 | 11 | var expectedTotal = 50 * 1 + 20 * 5 + 10 * 10 + 5 * 20; 12 | 13 | theBank.LogTillStatus(); 14 | Console.WriteLine(theBank); 15 | Console.WriteLine($"Expected till value: {expectedTotal}"); 16 | 17 | int transactions = 15; 18 | var valueGenerator = new Random((int)DateTime.Now.Ticks); 19 | 20 | while (transactions-- > 0) 21 | { 22 | int itemCost = valueGenerator.Next(2, 50); 23 | 24 | int numOnes = itemCost % 2; 25 | int numFives = (itemCost % 10 > 7) ? 1 : 0; 26 | int numTens = (itemCost % 20 > 13) ? 1 : 0; 27 | int numTwenties = (itemCost < 20) ? 1 : 2; 28 | 29 | try 30 | { 31 | Console.WriteLine($"Customer making a £{itemCost} purchase"); 32 | Console.WriteLine($"\t Using {numTwenties} twenties"); 33 | Console.WriteLine($"\t Using {numTens} tenners"); 34 | Console.WriteLine($"\t Using {numFives} fivers"); 35 | Console.WriteLine($"\t Using {numOnes} one-pound coins"); 36 | 37 | theBank.MakeChange(itemCost, numTwenties, numTens, numFives, numOnes); 38 | 39 | expectedTotal += itemCost; 40 | } 41 | catch (InvalidOperationException e) 42 | { 43 | Console.WriteLine($"Could not make transaction: {e.Message}"); 44 | } 45 | 46 | Console.WriteLine(theBank); 47 | Console.WriteLine($"Expected till value: {expectedTotal}"); 48 | Console.WriteLine(); 49 | } 50 | } 51 | } 52 | 53 | 54 | 55 | public class Till 56 | { 57 | private int OneDollarBills; 58 | private int FiveDollarBills; 59 | private int TenDollarBills; 60 | private int TwentyDollarBills; 61 | 62 | public Till(int ones, int fives, int tens = 0, int twenties = 0) => 63 | (OneDollarBills, FiveDollarBills, TenDollarBills, TwentyDollarBills) = 64 | (ones, fives, tens, twenties); 65 | 66 | public void MakeChange(int cost, int twenties, int tens = 0, int fives = 0, int ones = 0) 67 | { 68 | TwentyDollarBills += twenties; 69 | TenDollarBills += tens; 70 | FiveDollarBills += fives; 71 | OneDollarBills += ones; 72 | 73 | int amountPaid = twenties * 20 + tens * 10 + fives * 5 + ones; 74 | int changeNeeded = amountPaid - cost; 75 | 76 | if (changeNeeded < 0) 77 | throw new InvalidOperationException("Not enough money provided"); 78 | 79 | Console.WriteLine("Cashier Returns:"); 80 | 81 | while ((changeNeeded > 19) && (TwentyDollarBills > 0)) 82 | { 83 | TwentyDollarBills--; 84 | changeNeeded -= 20; 85 | Console.WriteLine("\t A twenty"); 86 | } 87 | 88 | while ((changeNeeded > 9) && (TenDollarBills > 0)) 89 | { 90 | TenDollarBills--; 91 | changeNeeded -= 10; 92 | Console.WriteLine("\t A tenner"); 93 | } 94 | 95 | while ((changeNeeded > 4) && (FiveDollarBills > 0)) 96 | { 97 | FiveDollarBills--; 98 | changeNeeded -= 5; 99 | Console.WriteLine("\t A fiver"); 100 | } 101 | 102 | while ((changeNeeded > 0) && (OneDollarBills > 0)) 103 | { 104 | OneDollarBills--; 105 | changeNeeded--; 106 | Console.WriteLine("\t A one"); 107 | } 108 | 109 | if (changeNeeded > 0) 110 | throw new InvalidOperationException("Can't make change. Do you have anything smaller?"); 111 | } 112 | 113 | public void LogTillStatus() 114 | { 115 | Console.WriteLine("The till currently has:"); 116 | Console.WriteLine($"{TwentyDollarBills * 20} in twenties"); 117 | Console.WriteLine($"{TenDollarBills * 10} in tens"); 118 | Console.WriteLine($"{FiveDollarBills * 5} in fives"); 119 | Console.WriteLine($"{OneDollarBills} in ones"); 120 | Console.WriteLine(); 121 | } 122 | 123 | public override string ToString() => 124 | $"The till has {TwentyDollarBills * 20 + TenDollarBills * 10 + FiveDollarBills * 5 + OneDollarBills} dollars"; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /3 - Exceptions and Errors/README.md: -------------------------------------------------------------------------------- 1 | # Exceptions Demo 2 | 3 | First, walk through the MakeChange routine at a conceptual level. Do not show the code. It sounds simple: look at the idea where you make change from the largest bills (since those are the ones people likely give you.) When you run out of larger bills, start using smaller ones. The functionality is simple and clearly defined. 4 | 5 | There are two error conditions: One is when the amount paid doesn't cover the cost of the items. The other is when the till doesn't have enough small bills to make change. Once again, stress the simplicity. 6 | 7 | It looks simple. Now, without looking at the implementation of the till class, show the test code in program.cs. 8 | 9 | Run the simulation with a small set of data, like you would in a unit test. It looks great. Now, change the number of iterations and try again. Point out the runtime problem: State has been mutated when there have been error conditions. If you run this over a longer period of time, it will always fail eventually. 10 | 11 | Why did this happen? Well, the code mutated state before it encountered an error condition. 12 | 13 | Key point: This is a small simulation that demonstrates a common problem in large code bases: Unless unit tests are carefully constructed, exceptions may not always occur in your test environments. It's only under production stress that these appear. Then, data errors happen with live customer data. It gets very expensive and becomes a data recovery issue. 14 | 15 | ## The lab 16 | 17 | Now, show the code for the make change method. The lab is to fix the code *so that it still throws exceptions in these error conditions*, but recovery is safe. That means the state of the till cannot change when the purchase can't be completed. 18 | 19 | There are a number of ways to implement the change, but all share some common themes discussed in the "practices" slide. 20 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark\Benchmark.csproj", "{F4CBDBBD-6D6F-4088-AB93-7EF03AC6BEE4}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {F4CBDBBD-6D6F-4088-AB93-7EF03AC6BEE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {F4CBDBBD-6D6F-4088-AB93-7EF03AC6BEE4}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {F4CBDBBD-6D6F-4088-AB93-7EF03AC6BEE4}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {F4CBDBBD-6D6F-4088-AB93-7EF03AC6BEE4}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | Latest 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/BenchmarkDotNet.Artifacts/results/Benchmark.Benchmarks-report-github.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.11.3, OS=macOS Mojave 10.14.3 (18D42) [Darwin 18.2.0] 4 | Intel Core i7-7920HQ CPU 3.10GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores 5 | .NET Core SDK=3.0.100-preview-010177 6 | [Host] : .NET Core ? (CoreCLR 4.6.0.0, CoreFX 4.6.26614.01), 64bit RyuJIT 7 | DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | 12 | |---------------------- |-----------:|----------:|----------:|-----------:| 13 | | ImmutableAddByType | 0.0191 ns | 0.0224 ns | 0.0210 ns | 0.0110 ns | 14 | | ImmutableAddByRefType | 0.3007 ns | 0.0381 ns | 0.0648 ns | 0.2939 ns | 15 | | MutableAddByType | 0.0105 ns | 0.0198 ns | 0.0186 ns | 0.0000 ns | 16 | | MutableAddByRefType | 17.6355 ns | 0.3840 ns | 0.4856 ns | 17.6148 ns | 17 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/BenchmarkDotNet.Artifacts/results/Benchmark.Benchmarks-report.csv: -------------------------------------------------------------------------------- 1 | Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Median 2 | ImmutableAddByType,Default,False,Default,Default,Default,Default,Default,Default,00000000,Empty,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,0.0191 ns,0.0224 ns,0.0210 ns,0.0110 ns 3 | ImmutableAddByRefType,Default,False,Default,Default,Default,Default,Default,Default,00000000,Empty,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,0.3007 ns,0.0381 ns,0.0648 ns,0.2939 ns 4 | MutableAddByType,Default,False,Default,Default,Default,Default,Default,Default,00000000,Empty,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,0.0105 ns,0.0198 ns,0.0186 ns,0.0000 ns 5 | MutableAddByRefType,Default,False,Default,Default,Default,Default,Default,Default,00000000,Empty,RyuJit,X64,Core,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,17.6355 ns,0.3840 ns,0.4856 ns,17.6148 ns 6 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/BenchmarkDotNet.Artifacts/results/Benchmark.Benchmarks-report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Benchmark.Benchmarks 6 | 7 | 13 | 14 | 15 |

16 | BenchmarkDotNet=v0.11.3, OS=macOS Mojave 10.14.3 (18D42) [Darwin 18.2.0]
17 | Intel Core i7-7920HQ CPU 3.10GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
18 | .NET Core SDK=3.0.100-preview-010177
19 |   [Host]     : .NET Core ? (CoreCLR 4.6.0.0, CoreFX 4.6.26614.01), 64bit RyuJIT
20 |   DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
21 | 
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
MethodMeanErrorStdDevMedian
ImmutableAddByType0.0191 ns0.0224 ns0.0210 ns0.0110 ns
ImmutableAddByRefType0.3007 ns0.0381 ns0.0648 ns0.2939 ns
MutableAddByType0.0105 ns0.0198 ns0.0186 ns0.0000 ns
MutableAddByRefType17.6355 ns0.3840 ns0.4856 ns17.6148 ns
32 | 33 | 34 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/LargeImmutableStruct.cs: -------------------------------------------------------------------------------- 1 | namespace Benchmark 2 | { 3 | readonly struct LargeImmutableStruct 4 | { 5 | private readonly double a; 6 | private readonly double b; 7 | private readonly double c; 8 | private readonly double d; 9 | private readonly double e; 10 | private readonly double f; 11 | private readonly double g; 12 | private readonly double h; 13 | 14 | public LargeImmutableStruct(double x, double y = 0, double z = 0) 15 | { 16 | X = x; 17 | Y = y; 18 | Z = z; 19 | a = 1; 20 | b = 2; 21 | c = 3; 22 | d = 4; 23 | e = 5; 24 | f = 6; 25 | g = 7; 26 | h = 8; 27 | } 28 | 29 | public double X { get; } 30 | public double Y { get; } 31 | public double Z { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/LargeMutableStruct.cs: -------------------------------------------------------------------------------- 1 | namespace Benchmark 2 | { 3 | struct LargeMutableStruct 4 | { 5 | private double a; 6 | private double b; 7 | private double c; 8 | private double d; 9 | private double e; 10 | private double f; 11 | private double g; 12 | private double h; 13 | 14 | public LargeMutableStruct(double x, double y = 0, double z = 0) 15 | { 16 | X = x; 17 | Y = y; 18 | Z = z; 19 | a = 1; 20 | b = 2; 21 | c = 3; 22 | d = 4; 23 | e = 5; 24 | f = 6; 25 | g = 7; 26 | h = 8; 27 | } 28 | 29 | public double X { get; } 30 | public double Y { get; } 31 | public double Z { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /4 - Memory And Performance/Lab/Benchmark/Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Running; 3 | 4 | namespace Benchmark 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | var Summary = BenchmarkRunner.Run(); 11 | } 12 | } 13 | 14 | public class Benchmarks 15 | { 16 | readonly LargeImmutableStruct immutableStruct = new LargeImmutableStruct(1.1, 2.2); 17 | readonly LargeMutableStruct mutableStruct = new LargeMutableStruct(1.1, 2.2); 18 | 19 | [Benchmark] 20 | public void ImmutableAddByType() => AddByType(immutableStruct); 21 | 22 | [Benchmark] 23 | public void ImmutableAddByRefType() => AddByRefType(in immutableStruct); 24 | 25 | [Benchmark] 26 | public void MutableAddByType() => AddByType(mutableStruct); 27 | 28 | [Benchmark] 29 | public void MutableAddByRefType() => AddByRefType(in mutableStruct); 30 | 31 | double AddByType(LargeImmutableStruct s) => s.X + s.Y; 32 | double AddByRefType(in LargeImmutableStruct s) => s.X + s.Y; 33 | double AddByType(LargeMutableStruct s) => s.X + s.Y; 34 | double AddByRefType(in LargeMutableStruct s) => s.X + s.Y; 35 | } 36 | } -------------------------------------------------------------------------------- /4 - Memory And Performance/Memory and Performance.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0cb18100b0faa2dc3094257f436f5642b3f84e77495d6f5d44ac12649665530f 3 | size 776971 4 | -------------------------------------------------------------------------------- /4 - Memory And Performance/README.md: -------------------------------------------------------------------------------- 1 | # In, out, ref, and benchmaks 2 | 3 | ## Steps to Execute Benchmark 4 | 5 | ### 1. Publish The Project 6 | 7 | Run the following command to create a self contained executable 8 | 9 | - Windows 64-bit OS 10 | 11 | ```console 12 | dotnet publish -c Release --self-contained -r win10-x64 13 | ``` 14 | 15 | - Windows 32-bit OS 16 | 17 | ```console 18 | dotnet publish -c Release --self-contained -r win10-x86 19 | ``` 20 | 21 | - macOS 22 | 23 | ```console 24 | dotnet publish ./Benchmark.csproj -c Release --self-contained -r osx-x64 25 | ``` 26 | 27 | ### 2. Run the Benchmark 28 | 29 | Run from the following command in the project directory 30 | 31 | - Windows 64-bit OS 32 | 33 | ```console 34 | ./bin/Release/netcoreapp2.0/win10-x64/benchmark.exe 35 | ``` 36 | 37 | - Windows 32-bit OS 38 | 39 | ```console 40 | ./bin/Release/netcoreapp2.0/win10-x32/benchmark.exe 41 | ``` 42 | 43 | - macOS 44 | 45 | ```console 46 | ./bin/Release/netcoreapp2.0/osx-x64/Benchmark 47 | ``` 48 | 49 | ## Sample Benchmark Results 50 | 51 | | Method | Mean | Error | StdDev | Median | 52 | |--------|------|-------|--------|--------| 53 | | ImmutableAddByType | 0.0191 ns | 0.0224 ns | 0.0210 ns | 0.0110 ns | 54 | | ImmutableAddByRefType | 0.3007 ns | 0.0381 ns | 0.0648 ns | 0.2939 ns | 55 | | MutableAddByType | 0.0105 ns | 0.0198 ns | 0.0186 ns | 0.0000 ns | 56 | | MutableAddByRefType | 17.6355 ns | 0.3840 ns | 0.4856 ns | 17.6148 ns | 57 | 58 | ## Lab Explanation 59 | 60 | In this lab, we will explore the following: 61 | 62 | - pass by 'ref' 63 | - pass uninitialized struct by 'out' 64 | 65 | Explore different sizes. For the last one, compare with 66 | members of your group because different machines may 67 | have different characteristics. 68 | 69 | Another question to explore if changing between 64 bit 70 | and 32 bit and independent makes a difference. How about 71 | .NET Framework and .NET Core? 72 | 73 | Consider making a ref struct. 74 | 75 | Consider adding interface implementation to the benchmark. How does that change the performance on structs, readonly structs, and ref struct (trick question, ref structs can't add interface implementation) 76 | 77 | ## Presenter Notes 78 | 79 | Run the benchmark program. explain how 80 | benchmark.net works. It's OSS, its on NuGet and it's a great 81 | tool to measure performance on low-level algorithms. 82 | 83 | As written, this compares four permutations: 84 | mutable struct, passed by value, 85 | mutable struct, pased by readonly ref, 86 | immutable struct, passed by value, 87 | immutable struct, passed by readonly ref. 88 | 89 | There are other permutations that apply and can help understand 90 | the nuances of different design choices. -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncBreakfast", "AsyncBreakfast\AsyncBreakfast.csproj", "{C5F6853B-063C-44EF-A73E-AE195649C74C}" 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 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.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 = {AFFDFE7B-425E-43AB-94BF-1E1C9D5A19C8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/AsyncBreakfast.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.0 6 | latest 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Bacon.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Bacon 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Coffee.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Coffee 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Egg.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Egg 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Juice.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Juice 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace AsyncBreakfast 5 | { 6 | class Program 7 | { 8 | static async Task Main(string[] args) 9 | { 10 | Coffee cup = PourCoffee(); 11 | Console.WriteLine("Coffee is ready"); 12 | 13 | var eggsTask = FryEggsAsync(2); 14 | var baconTask = FryBaconAsync(3); 15 | var toastTask = makeToastWithButterAndJamAsync(2); 16 | 17 | var eggs = await eggsTask; 18 | Console.WriteLine("Eggs are ready"); 19 | 20 | var bacon = await baconTask; 21 | Console.WriteLine("Bacon is ready"); 22 | 23 | var toast = await toastTask; 24 | Console.WriteLine("Toast is ready"); 25 | 26 | Juice oj = PourOJ(); 27 | Console.WriteLine("OJ is ready"); 28 | 29 | Console.WriteLine("Breakfast is ready!"); 30 | 31 | async Task makeToastWithButterAndJamAsync(int number) 32 | { 33 | var plainToast = await ToastBreadAsync(number); 34 | 35 | ApplyButter(plainToast); 36 | ApplyJam(plainToast); 37 | 38 | return plainToast; 39 | } 40 | } 41 | 42 | private static Juice PourOJ() 43 | { 44 | Console.WriteLine("Pouring Orange Juice"); 45 | return new Juice(); 46 | } 47 | 48 | private static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast"); 49 | 50 | private static void ApplyButter(Toast toast) => Console.WriteLine("Putting butter on the toast"); 51 | 52 | private static async Task ToastBreadAsync(int slices) 53 | { 54 | for (int slice = 0; slice < slices; slice++) 55 | Console.WriteLine("Putting a slice of bread in the toaster"); 56 | 57 | Console.WriteLine("Start toasting..."); 58 | await Task.Delay(3000); 59 | 60 | Console.WriteLine("Remove toast from toaster"); 61 | 62 | return new Toast(); 63 | } 64 | 65 | private static async Task FryBaconAsync(int slices) 66 | { 67 | Console.WriteLine($"Putting {slices} of bacon in the pan"); 68 | 69 | Console.WriteLine("Cooking first side of bacon..."); 70 | await Task.Delay(3000); 71 | 72 | for (int slice = 0; slice < slices; slice++) 73 | Console.WriteLine("Flipping a slice of bacon"); 74 | 75 | Console.WriteLine("Cooking the second side of bacon..."); 76 | await Task.Delay(3000); 77 | 78 | Console.WriteLine("Putting bacon on plate"); 79 | return new Bacon(); 80 | } 81 | 82 | private static async Task FryEggsAsync(int howMany) 83 | { 84 | Console.WriteLine("Warming the egg pan..."); 85 | await Task.Delay(3000); 86 | 87 | Console.WriteLine($"Cracking {howMany} eggs"); 88 | 89 | Console.WriteLine("Cooking the eggs ..."); 90 | await Task.Delay(3000); 91 | 92 | Console.WriteLine("Putting eggs on plate"); 93 | return new Egg(); 94 | } 95 | 96 | private static Coffee PourCoffee() 97 | { 98 | Console.WriteLine("Pouring coffee"); 99 | return new Coffee(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Completed/AsyncBreakfast/AsyncBreakfast/Toast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AsyncBreakfast 4 | { 5 | internal class Toast 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncBreakfast", "AsyncBreakfast\AsyncBreakfast.csproj", "{C5F6853B-063C-44EF-A73E-AE195649C74C}" 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 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.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 = {AFFDFE7B-425E-43AB-94BF-1E1C9D5A19C8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/AsyncBreakfast.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.0 6 | latest 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Bacon.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Bacon 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Coffee.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Coffee 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Egg.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Egg 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Juice.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Juice 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace AsyncBreakfast 5 | { 6 | class Program 7 | { 8 | static async Task Main(string[] args) 9 | { 10 | Coffee cup = PourCoffee(); 11 | Console.WriteLine("Coffee is ready"); 12 | 13 | Egg eggs = await FryEggs(2); 14 | Console.WriteLine("Eggs are ready"); 15 | 16 | Bacon bacon = await FryBacon(3); 17 | Console.WriteLine("Bacon is ready"); 18 | 19 | Toast toast = await ToastBread(2); 20 | 21 | ApplyButter(toast); 22 | ApplyJam(toast); 23 | 24 | Console.WriteLine("Toast is ready"); 25 | 26 | Juice oj = PourOJ(); 27 | 28 | Console.WriteLine("OJ is ready"); 29 | 30 | Console.WriteLine("Breakfast is ready!"); 31 | } 32 | 33 | private static Juice PourOJ() 34 | { 35 | Console.WriteLine("Pouring Orange Juice"); 36 | return new Juice(); 37 | } 38 | 39 | private static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast"); 40 | 41 | private static void ApplyButter(Toast toast) => Console.WriteLine("Putting butter on the toast"); 42 | 43 | private static async Task ToastBread(int slices) 44 | { 45 | for (int slice = 0; slice < slices; slice++) 46 | Console.WriteLine("Putting a slice of bread in the toaster"); 47 | 48 | Console.WriteLine("Start toasting..."); 49 | await Task.Delay(3000); 50 | 51 | Console.WriteLine("Removing toast from toaster"); 52 | 53 | return new Toast(); 54 | } 55 | 56 | private static async Task FryBacon(int slices) 57 | { 58 | Console.WriteLine($"Putting {slices} of bacon in the pan"); 59 | Console.WriteLine("Cooking first side of bacon..."); 60 | await Task.Delay(3000); 61 | 62 | for (int slice = 0; slice < slices; slice++) 63 | Console.WriteLine("Flipping a slice of bacon"); 64 | 65 | Console.WriteLine("Cooking the second side of bacon..."); 66 | await Task.Delay(3000); 67 | 68 | Console.WriteLine("Putting bacon on plate"); 69 | 70 | return new Bacon(); 71 | } 72 | 73 | private static async Task FryEggs(int howMany) 74 | { 75 | Console.WriteLine("Warming the egg pan..."); 76 | await Task.Delay(3000); 77 | 78 | Console.WriteLine($"Cracking {howMany} eggs"); 79 | 80 | Console.WriteLine("Cooking the eggs ..."); 81 | await Task.Delay(3000); 82 | 83 | Console.WriteLine("Putting eggs on plate"); 84 | 85 | return new Egg(); 86 | } 87 | 88 | private static Coffee PourCoffee() 89 | { 90 | Console.WriteLine("Pouring coffee"); 91 | return new Coffee(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/First Checkpoint/AsyncBreakfast/AsyncBreakfast/Toast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AsyncBreakfast 4 | { 5 | internal class Toast 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncBreakfast", "AsyncBreakfast\AsyncBreakfast.csproj", "{C5F6853B-063C-44EF-A73E-AE195649C74C}" 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 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C5F6853B-063C-44EF-A73E-AE195649C74C}.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 = {AFFDFE7B-425E-43AB-94BF-1E1C9D5A19C8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/AsyncBreakfast.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.0 6 | latest 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Bacon.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Bacon 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Coffee.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Coffee 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Egg.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Egg 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Juice.cs: -------------------------------------------------------------------------------- 1 | namespace AsyncBreakfast 2 | { 3 | internal class Juice 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace AsyncBreakfast 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | Coffee cup = PourCoffee(); 11 | Console.WriteLine("Coffee is ready"); 12 | 13 | Egg eggs = FryEggs(2); 14 | Console.WriteLine("Eggs are ready"); 15 | 16 | Bacon bacon = FryBacon(3); 17 | Console.WriteLine("Bacon is ready"); 18 | 19 | Toast toast = ToastBread(2); 20 | 21 | ApplyButter(toast); 22 | ApplyJam(toast); 23 | 24 | Console.WriteLine("Toast is ready"); 25 | 26 | Juice oj = PourOJ(); 27 | Console.WriteLine("OJ is ready"); 28 | 29 | Console.WriteLine("Breakfast is ready!"); 30 | } 31 | 32 | private static Juice PourOJ() 33 | { 34 | Console.WriteLine("Pouring Orange Juice"); 35 | return new Juice(); 36 | } 37 | 38 | private static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast"); 39 | 40 | private static void ApplyButter(Toast toast) => Console.WriteLine("Putting butter on the toast"); 41 | 42 | private static Toast ToastBread(int slices) 43 | { 44 | for (int slice = 0; slice < slices; slice++) 45 | Console.WriteLine("Putting a slice of bread in the toaster"); 46 | 47 | Console.WriteLine("Start toasting..."); 48 | 49 | Task.Delay(3000).Wait(); 50 | 51 | Console.WriteLine("Remove toast from toaster"); 52 | 53 | return new Toast(); 54 | } 55 | 56 | private static Bacon FryBacon(int slices) 57 | { 58 | Console.WriteLine($"Putting {slices} of bacon in the pan"); 59 | 60 | Console.WriteLine("Cooking first side of bacon..."); 61 | Task.Delay(3000).Wait(); 62 | 63 | for (int slice = 0; slice < slices; slice++) 64 | Console.WriteLine("Flipping a slice of bacon"); 65 | 66 | Console.WriteLine("Cooking the second side of bacon..."); 67 | Task.Delay(3000).Wait(); 68 | 69 | Console.WriteLine("Putting bacon on plate"); 70 | 71 | return new Bacon(); 72 | } 73 | 74 | private static Egg FryEggs(int howMany) 75 | { 76 | Console.WriteLine("Warming the egg pan..."); 77 | Task.Delay(3000).Wait(); 78 | 79 | Console.WriteLine($"Cracking {howMany} eggs"); 80 | 81 | Console.WriteLine("Cooking the eggs ..."); 82 | Task.Delay(3000).Wait(); 83 | 84 | Console.WriteLine("Putting eggs on plate"); 85 | 86 | return new Egg(); 87 | } 88 | 89 | private static Coffee PourCoffee() 90 | { 91 | Console.WriteLine("Pouring coffee"); 92 | return new Coffee(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /5 - One Abstraction/Labs/Started/AsyncBreakfast/AsyncBreakfast/Toast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AsyncBreakfast 4 | { 5 | internal class Toast 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /5 - One Abstraction/One Abstraction.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0d4070300364335b00cc990e4ef0a0c8dcbfc663027d232c67ccbfa5ce5b33b4 3 | size 781206 4 | -------------------------------------------------------------------------------- /5 - One Abstraction/README.md: -------------------------------------------------------------------------------- 1 | # Async Breakfast demo 2 | 3 | Open the code from the asyncbreakfast-starter.zip 4 | 5 | Run the program. Discuss what's happening. Point out that each long-running task runs to completion before starting the next long running task. That means a cold breakfast, and inefficient use of resources. 6 | 7 | ## Incorporate basic async / await 8 | 9 | Open Program.cs. In turn, make the following to each long-running task: 10 | 11 | 1. Append `Async` to the method signature. 12 | 1. Change the `.Wait()` calls to `await` expressions in each call to `Task.Delay`. Point out that `Task.Delay` simulates some operation that takes time. 13 | 1. Add the `async` modifier to the method. 14 | 1. Change the return type to `Task` where `T` is the previous return type. 15 | 1. Note the new warning in the `Main` method, and add an `await` expression. 16 | 17 | After making all the changes in the first method, note that the `Main` method now has a warning. Take the suggestion to upgrade the project so that `async Task Main` is a valid entry point signature. The program should look like this: 18 | 19 | ```csharp 20 | class Program 21 | { 22 | static async Task Main(string[] args) 23 | { 24 | Coffee cup = PourCoffee(); 25 | Console.WriteLine("coffee is ready"); 26 | Egg eggs = await FryEggs(2); 27 | Console.WriteLine("eggs are ready"); 28 | Bacon bacon = await FryBacon(3); 29 | Console.WriteLine("bacon is ready"); 30 | Toast toast = await ToastBread(2); 31 | ApplyButter(toast); 32 | ApplyJam(toast); 33 | Console.WriteLine("toast is ready"); 34 | Juice oj = PourOJ(); 35 | Console.WriteLine("oj is ready"); 36 | 37 | Console.WriteLine("Breakfast is ready!"); 38 | } 39 | 40 | private static Juice PourOJ() 41 | { 42 | Console.WriteLine("Pouring Orange Juice"); 43 | return new Juice(); 44 | } 45 | 46 | private static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast"); 47 | 48 | private static void ApplyButter(Toast toast) => Console.WriteLine("Putting butter on the toast"); 49 | 50 | private static async Task ToastBread(int slices) 51 | { 52 | for (int slice = 0; slice < slices; slice++) 53 | Console.WriteLine("Putting a slice of bread in the toaster"); 54 | Console.WriteLine("Start toasting..."); 55 | await Task.Delay(3000); 56 | Console.WriteLine("Remove toast from toaster"); 57 | return new Toast(); 58 | } 59 | 60 | private static async Task FryBacon(int slices) 61 | { 62 | Console.WriteLine($"putting {slices} of bacon in the pan"); 63 | Console.WriteLine("cooking first side of bacon..."); 64 | await Task.Delay(3000); 65 | for (int slice = 0; slice < slices; slice++) 66 | Console.WriteLine("flipping a slice of bacon"); 67 | Console.WriteLine("cooking the second side of bacon..."); 68 | await Task.Delay(3000); 69 | Console.WriteLine("Put bacon on plate"); 70 | return new Bacon(); 71 | } 72 | 73 | private static async Task FryEggs(int howMany) 74 | { 75 | Console.WriteLine("Warming the egg pan..."); 76 | await Task.Delay(3000); 77 | Console.WriteLine($"cracking {howMany} eggs"); 78 | Console.WriteLine("cooking the eggs ..."); 79 | await Task.Delay(3000); 80 | Console.WriteLine("Put eggs on plate"); 81 | return new Egg(); 82 | } 83 | 84 | private static Coffee PourCoffee() 85 | { 86 | Console.WriteLine("Pouring coffee"); 87 | return new Coffee(); 88 | } 89 | } 90 | ``` 91 | 92 | Run the program again. Notice that nothing really changes. Explain that this is because each task is `await`-ed before the the next task completes. In cloud scenarios, that might be fine. The CPU resource could now start tasks for other web requests while awaiting. In this scenario, there is other work to do, so let's continue refactoring a bit. 93 | 94 | ## Start multiple tasks as soon as possible 95 | 96 | The next set of changes is to start subsequent tasks before the preceding task has finished, when possible. Change each await as in the following example: 97 | 98 | ```csharp 99 | Egg eggs = await FryEggs(2); 100 | ``` 101 | 102 | Should change to: 103 | 104 | ```csharp 105 | Task eggTask = FryEggs(2); 106 | Egg eggs = await eggTask; 107 | ``` 108 | 109 | Then, move all the `await` statements below the lines that start each task. Run it. Now, it finishes faster, because you're starting tasks while awaiting asynchronous work to finish. POint out that this is more like the way people actually make breakfast. 110 | 111 | ## Compose tasks 112 | 113 | The toast illustrates a common problem developers have composing task-based work: There is an asynchronous part, followed by a synchronous part. You want to add butter and jam as soon as the toast finishes, without waiting for the bacon and eggs to be done. 114 | 115 | This could be done with `Task.ContinueWith()`, but there's a better way: refactor it to take advantage of new C# language features, like local functions. Move the toast making code to a local function in `Main`: 116 | 117 | ```csharp 118 | async Task makeToastWithButterAndJamAsync(int number) 119 | { 120 | var plainToast = await ToastBreadAsync(number); 121 | ApplyButter(plainToast); 122 | ApplyJam(plainToast); 123 | return plainToast; 124 | } 125 | ``` 126 | 127 | Then, change `Main` to use the new function: 128 | 129 | ```csharp 130 | var toastTask = makeToastWithButterAndJamAsync(2); 131 | // ... 132 | var toast = await toastTask; 133 | Console.WriteLine("toast is ready"); 134 | ``` 135 | 136 | That's done. Call it good. 137 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Completed/IssuePRreport/IssuePRreport.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IssuePRreport", "IssuePRreport\IssuePRreport.csproj", "{3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}" 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 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.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 = {76E58366-FA03-4273-8738-D27981692F6A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Completed/IssuePRreport/IssuePRreport/IssuePRreport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Completed/IssuePRreport/IssuePRreport/Program.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using Octokit; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace GitHubActivityReport 10 | { 11 | class GraphQLRequest 12 | { 13 | [JsonProperty("query")] 14 | public string Query { get; set; } 15 | 16 | [JsonProperty("variables")] 17 | public IDictionary Variables { get; } = new Dictionary(); 18 | 19 | public string ToJsonText() => 20 | JsonConvert.SerializeObject(this); 21 | } 22 | 23 | static class Queries 24 | { 25 | internal const string IssueQuery = 26 | @"query ($repo_name: String!) { 27 | repository(owner: ""dotnet"", name: $repo_name) { 28 | issues(last: 50) 29 | { 30 | nodes { 31 | title 32 | number 33 | createdAt 34 | } 35 | } 36 | } 37 | } 38 | "; 39 | internal const string PullRequestQuery = 40 | @"query($repo_name:String!) { 41 | repository(owner: ""dotnet"", name: $repo_name) { 42 | pullRequests(last: 50) { 43 | nodes { 44 | title 45 | number 46 | createdAt 47 | } 48 | } 49 | } 50 | }"; 51 | } 52 | 53 | class Program 54 | { 55 | static async Task Main(string[] args) 56 | { 57 | //Follow these steps to create a GitHub Access Token https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token 58 | //Select the following permissions for your GitHub Access Token: 59 | // - repo:status 60 | // - public_repo 61 | var key = GetEnvVariable("GitHubKey", 62 | "You must store you GitHub key in the 'GitHubKey' environment variable", 63 | ""); 64 | 65 | var client = new GitHubClient(new Octokit.ProductHeaderValue("IssueQueryDemo")) 66 | { 67 | Credentials = new Octokit.Credentials(key) 68 | }; 69 | 70 | // Next: Run the issue query. 71 | await SimpleRunQuery(client); 72 | 73 | await IssuesThenPRsQuery(client); 74 | 75 | await PrintInOrderFinished(client); 76 | 77 | Console.ReadLine(); 78 | } 79 | 80 | static async Task SimpleRunQuery(GitHubClient client) 81 | { 82 | JObject results = await RunQuery(client, Queries.IssueQuery, "docs"); 83 | Console.WriteLine(results); 84 | 85 | results = await RunQuery(client, Queries.IssueQuery, "dotnet-api-docs"); 86 | Console.WriteLine(results); 87 | 88 | // Find PRs: 89 | results = await RunQuery(client, Queries.PullRequestQuery, "samples"); 90 | Console.WriteLine(results); 91 | 92 | results = await RunQuery(client, Queries.PullRequestQuery, "dotnet-api-docs"); 93 | Console.WriteLine(results); 94 | 95 | results = await RunQuery(client, Queries.PullRequestQuery, "docs"); 96 | Console.WriteLine(results); 97 | } 98 | 99 | static async Task IssuesThenPRsQuery(GitHubClient client) 100 | { 101 | var docsIssueTask = RunQuery(client, Queries.IssueQuery, "docs"); 102 | 103 | var apidocsIssueTask = RunQuery(client, Queries.IssueQuery, "dotnet-api-docs"); 104 | 105 | // Find PRs: 106 | var samplesPRTask = RunQuery(client, Queries.PullRequestQuery, "samples"); 107 | var apiDocsPRTask = RunQuery(client, Queries.PullRequestQuery, "dotnet-api-docs"); 108 | var docsPRTask = RunQuery(client, Queries.PullRequestQuery, "docs"); 109 | 110 | writeData(await docsIssueTask); 111 | writeData(await apidocsIssueTask); 112 | writeData(await samplesPRTask); 113 | writeData(await apiDocsPRTask); 114 | writeData(await docsPRTask); 115 | 116 | void writeData(JObject data) => Console.WriteLine(data); 117 | } 118 | 119 | static async Task PrintInOrderFinished(GitHubClient client) 120 | { 121 | List> queryTasks = new List> 122 | { 123 | RunQuery(client, Queries.IssueQuery, "docs"), 124 | RunQuery(client, Queries.IssueQuery, "dotnet-api-docs"), 125 | RunQuery(client, Queries.PullRequestQuery, "samples"), 126 | RunQuery(client, Queries.PullRequestQuery, "dotnet-api-docs"), 127 | RunQuery(client, Queries.PullRequestQuery, "docs") 128 | }; 129 | 130 | while (queryTasks.Any()) 131 | { 132 | var finished = await Task.WhenAny(queryTasks); 133 | writeData(await finished); 134 | queryTasks.Remove(finished); 135 | } 136 | 137 | void writeData(JObject data) => Console.WriteLine(data); 138 | } 139 | 140 | static async Task RunQuery(GitHubClient client, string queryText, string repoName) 141 | { 142 | if (client == null) 143 | throw new ArgumentNullException(paramName: nameof(client), "bad null client"); 144 | if (string.IsNullOrWhiteSpace(queryText)) 145 | throw new ArgumentNullException(); 146 | if (string.IsNullOrWhiteSpace(repoName)) 147 | throw new ArgumentNullException(); 148 | 149 | return runQueryImpl(); 150 | 151 | async Task runQueryImpl() 152 | { 153 | Query = queryText 154 | }; 155 | issueAndPRQuery.Variables["repo_name"] = repoName; 156 | 157 | var postBody = issueAndPRQuery.ToJsonText(); 158 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 159 | postBody, "application/json", "application/json"); 160 | 161 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 162 | return results; 163 | } 164 | } 165 | 166 | static string GetEnvVariable(string item, string error, string defaultValue) 167 | { 168 | var value = Environment.GetEnvironmentVariable(item); 169 | if (string.IsNullOrWhiteSpace(value)) 170 | { 171 | if (!string.IsNullOrWhiteSpace(error)) 172 | { 173 | Console.WriteLine(error); 174 | Environment.Exit(0); 175 | } 176 | 177 | if (!string.IsNullOrWhiteSpace(defaultValue)) 178 | { 179 | return defaultValue; 180 | } 181 | } 182 | return value; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Started/IssuePRreport/IssuePRreport.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IssuePRreport", "IssuePRreport\IssuePRreport.csproj", "{3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}" 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 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.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 = {76E58366-FA03-4273-8738-D27981692F6A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Started/IssuePRreport/IssuePRreport/IssuePRreport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /6 - Task Composition/Lab/Started/IssuePRreport/IssuePRreport/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace GitHubActivityReport 8 | { 9 | class GraphQLRequest 10 | { 11 | [JsonProperty("query")] 12 | public string Query { get; set; } 13 | 14 | [JsonProperty("variables")] 15 | public IDictionary Variables { get; } = new Dictionary(); 16 | 17 | public string ToJsonText() => 18 | JsonConvert.SerializeObject(this); 19 | } 20 | 21 | static class Queries 22 | { 23 | internal const string IssueQuery = 24 | @"query ($repo_name: String!) { 25 | repository(owner: ""dotnet"", name: $repo_name) { 26 | issues(last: 50) 27 | { 28 | nodes { 29 | title 30 | number 31 | createdAt 32 | } 33 | } 34 | } 35 | } 36 | "; 37 | internal const string PullRequestQuery = 38 | @"query($repo_name:String!) { 39 | repository(owner: ""dotnet"", name: $repo_name) { 40 | pullRequests(last: 50) { 41 | nodes { 42 | title 43 | number 44 | createdAt 45 | } 46 | } 47 | } 48 | }"; 49 | } 50 | 51 | class Program 52 | { 53 | static async Task Main(string[] args) 54 | { 55 | //Follow these steps to create a GitHub Access Token https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token 56 | //Select the following permissions for your GitHub Access Token: 57 | // - repo:status 58 | // - public_repo 59 | var key = GetEnvVariable("GitHubKey", 60 | "You must store you GitHub key in the 'GitHubKey' environment variable", 61 | ""); 62 | 63 | var client = new Octokit.GitHubClient(new Octokit.ProductHeaderValue("IssueQueryDemo")) 64 | { 65 | Credentials = new Octokit.Credentials(key) 66 | }; 67 | 68 | // Next: Run the issue query. 69 | var issueAndPRQuery = new GraphQLRequest 70 | { 71 | Query = Queries.IssueQuery 72 | }; 73 | issueAndPRQuery.Variables["repo_name"] = "docs"; 74 | 75 | var postBody = issueAndPRQuery.ToJsonText(); 76 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 77 | postBody, "application/json", "application/json"); 78 | 79 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 80 | Console.WriteLine(results); 81 | 82 | // Find PRs: 83 | issueAndPRQuery.Query = Queries.PullRequestQuery; 84 | issueAndPRQuery.Variables["repo_name"] = "docs"; 85 | 86 | postBody = issueAndPRQuery.ToJsonText(); 87 | response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 88 | postBody, "application/json", "application/json"); 89 | 90 | results = JObject.Parse(response.HttpResponse.Body.ToString()); 91 | Console.WriteLine(results); 92 | 93 | // TODO as a lab: 94 | // 1. Find the latest 50 Open issues in the dotnet/docs repo. 95 | // 2. Find the latest 50 in dotnet-api-docs 96 | // 3. Do the same for PRs in: dotnet/docs, dotnet/dotnet-api-docs, dotnet/samples 97 | 98 | Console.ReadLine(); 99 | } 100 | 101 | static string GetEnvVariable(string item, string error, string defaultValue) 102 | { 103 | var value = Environment.GetEnvironmentVariable(item); 104 | if (string.IsNullOrWhiteSpace(value)) 105 | { 106 | if (!string.IsNullOrWhiteSpace(error)) 107 | { 108 | Console.WriteLine(error); 109 | Environment.Exit(0); 110 | } 111 | 112 | if (!string.IsNullOrWhiteSpace(defaultValue)) 113 | { 114 | return defaultValue; 115 | } 116 | } 117 | return value; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /6 - Task Composition/README.md: -------------------------------------------------------------------------------- 1 | # Morning Async / Await lab 2 | 3 | ## Get Issues 4 | 5 | Start with the skeleton, and get the most recent 50 issues created for 6 | 7 | -dotnet/docs 8 | -dotnet/dotnet-api-docs 9 | 10 | Then, get the most recent merged 50 PRs for: 11 | 12 | -dotnet/docs 13 | -dotnet/dotnet-api-docs 14 | -dotnet/samples 15 | 16 | Print out pertitent information for each. 17 | 18 | Once you've got the basics working, try to control order of output in the following ways: 19 | 20 | 1. Print all issues first, then PRs 21 | 1. Print all PRs first, then issues. 22 | 1. First print whatever task finished first. 23 | 24 | ## Interface / API critique 25 | 26 | How would you create a class hierarchy for these APIs? What operations should be synchronous? What should be asynchronous? 27 | 28 | How does that shape construction, properties, and other elements of APIs? 29 | 30 | Some thoughts: 31 | Are async APIs factories that *produce* results as new objects? 32 | Should async APIs perform *actions* that modifying existing objects? 33 | -------------------------------------------------------------------------------- /6 - Task Composition/Task Composition.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5db9a6e3a26ca8519609afd6ab9eb809d7604fd876511d595256b0778155b713 3 | size 786314 4 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Advanced Task Composition.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:99d9dafc43b013b28acf84b6a322d5fb6a74f953131665b78252a382bedfc2e0 3 | size 784934 4 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Completed/IssuePRreport/IssuePRreport.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IssuePRreport", "IssuePRreport\IssuePRreport.csproj", "{3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}" 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 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.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 = {76E58366-FA03-4273-8738-D27981692F6A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Completed/IssuePRreport/IssuePRreport/IssuePRreport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Completed/IssuePRreport/IssuePRreport/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using Octokit; 9 | 10 | namespace GitHubActivityReport 11 | { 12 | class GraphQLRequest 13 | { 14 | [JsonProperty("query")] 15 | public string Query { get; set; } 16 | 17 | [JsonProperty("variables")] 18 | public IDictionary Variables { get; } = new Dictionary(); 19 | 20 | public string ToJsonText() => 21 | JsonConvert.SerializeObject(this); 22 | } 23 | 24 | static class GraphQLQueries 25 | { 26 | internal const string IssueQuery = 27 | @"query ($repo_name: String!) { 28 | repository(owner: ""dotnet"", name: $repo_name) { 29 | issues(last: 50) 30 | { 31 | nodes { 32 | title 33 | number 34 | createdAt 35 | } 36 | } 37 | } 38 | } 39 | "; 40 | internal const string PullRequestQuery = 41 | @"query($repo_name:String!) { 42 | repository(owner: ""dotnet"", name: $repo_name) { 43 | pullRequests(last: 50) { 44 | nodes { 45 | title 46 | number 47 | createdAt 48 | } 49 | } 50 | } 51 | }"; 52 | internal const string PagedIssueQuery = 53 | @"query ($repo_name: String!, $start_cursor:String) { 54 | repository(owner: ""dotnet"", name: $repo_name) { 55 | issues(last: 25, before: $start_cursor) 56 | { 57 | totalCount 58 | pageInfo { 59 | hasPreviousPage 60 | startCursor 61 | } 62 | nodes { 63 | title 64 | number 65 | createdAt 66 | } 67 | } 68 | } 69 | } 70 | "; 71 | } 72 | 73 | class Program 74 | { 75 | static async Task Main(string[] args) 76 | { 77 | //Follow these steps to create a GitHub Access Token https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token 78 | //Select the following permissions for your GitHub Access Token: 79 | // - repo:status 80 | // - public_repo 81 | var key = GetEnvVariable("GitHubKey", 82 | "You must store you GitHub key in the 'GitHubKey' environment variable", 83 | ""); 84 | 85 | var client = new GitHubClient(new Octokit.ProductHeaderValue("IssueQueryDemo")) 86 | { 87 | Credentials = new Octokit.Credentials(key) 88 | }; 89 | 90 | // Next: Run the issue query. 91 | //await SimpleRunQuery(client); 92 | 93 | // await IssuesThenPRsQuery(client); 94 | 95 | //await PrintInOrderFinished(client); 96 | 97 | CancellationTokenSource cancellationSource = new CancellationTokenSource(); 98 | var progressReporter = new ProgressStatus((num) => 99 | { 100 | Console.WriteLine($"Received {num} issues in total"); 101 | if (num > 100) 102 | cancellationSource.Cancel(); 103 | }); 104 | 105 | try 106 | { 107 | var results = await RunPagedQuery(client, GraphQLQueries.PagedIssueQuery, "docs", 108 | cancellationSource.Token, progressReporter); 109 | Console.WriteLine(results); 110 | } 111 | catch (OperationCanceledException) 112 | { 113 | Console.WriteLine("Work has been cancelled"); 114 | } 115 | 116 | Console.ReadLine(); 117 | } 118 | 119 | static async Task SimpleRunQuery(GitHubClient client) 120 | { 121 | JObject results = await RunQuery(client, GraphQLQueries.IssueQuery, "docs"); 122 | Console.WriteLine(results); 123 | 124 | results = await RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"); 125 | Console.WriteLine(results); 126 | 127 | // Find PRs: 128 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"); 129 | Console.WriteLine(results); 130 | 131 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"); 132 | Console.WriteLine(results); 133 | 134 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "docs"); 135 | Console.WriteLine(results); 136 | } 137 | 138 | static async Task IssuesThenPRsQuery(GitHubClient client) 139 | { 140 | var docsIssueTask = RunQuery(client, GraphQLQueries.IssueQuery, "docs"); 141 | 142 | var apidocsIssueTask = RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"); 143 | 144 | // Find PRs: 145 | var samplesPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"); 146 | var apiDocsPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"); 147 | var docsPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "docs"); 148 | 149 | writeData(await docsIssueTask); 150 | writeData(await apidocsIssueTask); 151 | writeData(await samplesPRTask); 152 | writeData(await apiDocsPRTask); 153 | writeData(await docsPRTask); 154 | 155 | void writeData(JObject data) => Console.WriteLine(data); 156 | } 157 | 158 | static async Task PrintInOrderFinished(GitHubClient client) 159 | { 160 | List> queryTasks = new List> 161 | { 162 | RunQuery(client, GraphQLQueries.IssueQuery, "docs"), 163 | RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"), 164 | RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"), 165 | RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"), 166 | RunQuery(client, GraphQLQueries.PullRequestQuery, "docs") 167 | }; 168 | 169 | while (queryTasks.Any()) 170 | { 171 | var finished = await Task.WhenAny(queryTasks); 172 | writeData(await finished); 173 | queryTasks.Remove(finished); 174 | } 175 | 176 | void writeData(JObject data) => Console.WriteLine(data); 177 | } 178 | 179 | static async Task RunQuery(GitHubClient client, string queryText, string repoName) 180 | { 181 | var issueAndPRQuery = new GraphQLRequest 182 | { 183 | Query = queryText 184 | }; 185 | issueAndPRQuery.Variables["repo_name"] = repoName; 186 | 187 | var postBody = issueAndPRQuery.ToJsonText(); 188 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 189 | postBody, "application/json", "application/json"); 190 | 191 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 192 | 193 | return results; 194 | } 195 | 196 | static async Task RunPagedQuery(GitHubClient client, string queryText, string repoName) 197 | { 198 | var issueAndPRQuery = new GraphQLRequest 199 | { 200 | Query = queryText 201 | }; 202 | issueAndPRQuery.Variables["repo_name"] = repoName; 203 | 204 | JArray finalResults = new JArray(); 205 | bool hasMorePages = true; 206 | int pagesReturned = 0; 207 | 208 | // Stop with 10 pages, because these are big repos: 209 | while (hasMorePages && (pagesReturned++ < 10)) 210 | { 211 | var postBody = issueAndPRQuery.ToJsonText(); 212 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 213 | postBody, "application/json", "application/json"); 214 | 215 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 216 | 217 | hasMorePages = (bool)results["data"]["repository"]["issues"]["pageInfo"]["hasPreviousPage"]; 218 | issueAndPRQuery.Variables["start_cursor"] = results["data"]["repository"]["issues"]["pageInfo"]["startCursor"].ToString(); 219 | 220 | finalResults.Merge(results["data"]["repository"]["issues"]["nodes"]); 221 | } 222 | 223 | return finalResults; 224 | } 225 | 226 | static async Task RunPagedQuery(GitHubClient client, string queryText, string repoName, CancellationToken cancel, IProgress progress) 227 | { 228 | var issueAndPRQuery = new GraphQLRequest 229 | { 230 | Query = queryText 231 | }; 232 | issueAndPRQuery.Variables["repo_name"] = repoName; 233 | 234 | JArray finalResults = new JArray(); 235 | bool hasMorePages = true; 236 | int pagesReturned = 0; 237 | int issuesReturned = 0; 238 | 239 | // Stop with 10 pages, because these are big repos: 240 | while (hasMorePages && (pagesReturned++ < 10)) 241 | { 242 | var postBody = issueAndPRQuery.ToJsonText(); 243 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 244 | postBody, "application/json", "application/json"); 245 | 246 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 247 | 248 | int totalCount = (int)results["data"]["repository"]["issues"]["totalCount"]; 249 | hasMorePages = (bool)results["data"]["repository"]["issues"]["pageInfo"]["hasPreviousPage"]; 250 | issueAndPRQuery.Variables["start_cursor"] = results["data"]["repository"]["issues"]["pageInfo"]["startCursor"].ToString(); 251 | 252 | finalResults.Merge(results["data"]["repository"]["issues"]["nodes"]); 253 | 254 | issuesReturned += results["data"]["repository"]["issues"]["nodes"].Count(); 255 | 256 | progress?.Report(issuesReturned); 257 | 258 | cancel.ThrowIfCancellationRequested(); 259 | } 260 | 261 | return finalResults; 262 | } 263 | 264 | static string GetEnvVariable(string item, string error, string defaultValue) 265 | { 266 | var value = Environment.GetEnvironmentVariable(item); 267 | 268 | if (string.IsNullOrWhiteSpace(value)) 269 | { 270 | if (!string.IsNullOrWhiteSpace(error)) 271 | { 272 | Console.WriteLine(error); 273 | Environment.Exit(0); 274 | } 275 | 276 | if (!string.IsNullOrWhiteSpace(defaultValue)) 277 | { 278 | return defaultValue; 279 | } 280 | } 281 | 282 | return value; 283 | } 284 | 285 | class ProgressStatus : IProgress 286 | { 287 | readonly Action action; 288 | 289 | public ProgressStatus(Action progressAction) => 290 | action = progressAction; 291 | 292 | public void Report(int value) => action?.Invoke(value); 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Started/IssuePRreport/IssuePRreport.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IssuePRreport", "IssuePRreport\IssuePRreport.csproj", "{3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}" 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 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3D5BFCF6-5A56-4E0B-9E29-BDAE8315AB29}.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 = {76E58366-FA03-4273-8738-D27981692F6A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Started/IssuePRreport/IssuePRreport/IssuePRreport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/Lab/Started/IssuePRreport/IssuePRreport/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using Octokit; 9 | 10 | namespace GitHubActivityReport 11 | { 12 | class GraphQLRequest 13 | { 14 | [JsonProperty("query")] 15 | public string Query { get; set; } 16 | 17 | [JsonProperty("variables")] 18 | public IDictionary Variables { get; } = new Dictionary(); 19 | 20 | public string ToJsonText() => 21 | JsonConvert.SerializeObject(this); 22 | } 23 | 24 | static class GraphQLQueries 25 | { 26 | internal const string IssueQuery = 27 | @"query ($repo_name: String!) { 28 | repository(owner: ""dotnet"", name: $repo_name) { 29 | issues(last: 50) 30 | { 31 | nodes { 32 | title 33 | number 34 | createdAt 35 | } 36 | } 37 | } 38 | } 39 | "; 40 | internal const string PullRequestQuery = 41 | @"query($repo_name:String!) { 42 | repository(owner: ""dotnet"", name: $repo_name) { 43 | pullRequests(last: 50) { 44 | nodes { 45 | title 46 | number 47 | createdAt 48 | } 49 | } 50 | } 51 | }"; 52 | internal const string PagedIssueQuery = 53 | @"query ($repo_name: String!, $start_cursor:String) { 54 | repository(owner: ""dotnet"", name: $repo_name) { 55 | issues(last: 25, before: $start_cursor) 56 | { 57 | totalCount 58 | pageInfo { 59 | hasPreviousPage 60 | startCursor 61 | } 62 | nodes { 63 | title 64 | number 65 | createdAt 66 | } 67 | } 68 | } 69 | } 70 | "; 71 | } 72 | 73 | class Program 74 | { 75 | static async Task Main(string[] args) 76 | { 77 | //Follow these steps to create a GitHub Access Token https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token 78 | //Select the following permissions for your GitHub Access Token: 79 | // - repo:status 80 | // - public_repo 81 | var key = GetEnvVariable("GitHubKey", 82 | "You must store you GitHub key in the 'GitHubKey' environment variable", 83 | ""); 84 | 85 | var client = new GitHubClient(new ProductHeaderValue("IssueQueryDemo")) 86 | { 87 | Credentials = new Credentials(key) 88 | }; 89 | 90 | // Next: Run the issue query. 91 | //await SimpleRunQuery(client); 92 | 93 | // await IssuesThenPRsQuery(client); 94 | 95 | //await PrintInOrderFinished(client); 96 | 97 | try 98 | { 99 | var results = await RunPagedQuery(client, GraphQLQueries.PagedIssueQuery, "docs"); 100 | Console.WriteLine(results); 101 | } 102 | catch (OperationCanceledException) 103 | { 104 | Console.WriteLine("Work has been cancelled"); 105 | } 106 | 107 | Console.ReadLine(); 108 | } 109 | 110 | static async Task SimpleRunQuery(GitHubClient client) 111 | { 112 | JObject results = await RunQuery(client, GraphQLQueries.IssueQuery, "docs"); 113 | Console.WriteLine(results); 114 | 115 | results = await RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"); 116 | Console.WriteLine(results); 117 | 118 | // Find PRs: 119 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"); 120 | Console.WriteLine(results); 121 | 122 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"); 123 | Console.WriteLine(results); 124 | 125 | results = await RunQuery(client, GraphQLQueries.PullRequestQuery, "docs"); 126 | Console.WriteLine(results); 127 | } 128 | 129 | static async Task IssuesThenPRsQuery(GitHubClient client) 130 | { 131 | var docsIssueTask = RunQuery(client, GraphQLQueries.IssueQuery, "docs"); 132 | 133 | var apidocsIssueTask = RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"); 134 | 135 | // Find PRs: 136 | var samplesPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"); 137 | var apiDocsPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"); 138 | var docsPRTask = RunQuery(client, GraphQLQueries.PullRequestQuery, "docs"); 139 | 140 | writeData(await docsIssueTask); 141 | writeData(await apidocsIssueTask); 142 | writeData(await samplesPRTask); 143 | writeData(await apiDocsPRTask); 144 | writeData(await docsPRTask); 145 | 146 | void writeData(JObject data) => Console.WriteLine(data); 147 | } 148 | 149 | static async Task PrintInOrderFinished(GitHubClient client) 150 | { 151 | List> queryTasks = new List> 152 | { 153 | RunQuery(client, GraphQLQueries.IssueQuery, "docs"), 154 | RunQuery(client, GraphQLQueries.IssueQuery, "dotnet-api-docs"), 155 | RunQuery(client, GraphQLQueries.PullRequestQuery, "samples"), 156 | RunQuery(client, GraphQLQueries.PullRequestQuery, "dotnet-api-docs"), 157 | RunQuery(client, GraphQLQueries.PullRequestQuery, "docs") 158 | }; 159 | 160 | while (queryTasks.Any()) 161 | { 162 | var finished = await Task.WhenAny(queryTasks); 163 | writeData(await finished); 164 | queryTasks.Remove(finished); 165 | } 166 | 167 | void writeData(JObject data) => Console.WriteLine(data); 168 | } 169 | 170 | static async Task RunQuery(GitHubClient client, string queryText, string repoName) 171 | { 172 | var issueAndPRQuery = new GraphQLRequest 173 | { 174 | Query = queryText 175 | }; 176 | issueAndPRQuery.Variables["repo_name"] = repoName; 177 | 178 | var postBody = issueAndPRQuery.ToJsonText(); 179 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 180 | postBody, "application/json", "application/json"); 181 | 182 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 183 | 184 | return results; 185 | } 186 | 187 | static async Task RunPagedQuery(GitHubClient client, string queryText, string repoName) 188 | { 189 | var issueAndPRQuery = new GraphQLRequest 190 | { 191 | Query = queryText 192 | }; 193 | issueAndPRQuery.Variables["repo_name"] = repoName; 194 | 195 | JArray finalResults = new JArray(); 196 | bool hasMorePages = true; 197 | int pagesReturned = 0; 198 | 199 | // Stop with 10 pages, because these are big repos: 200 | while (hasMorePages && (pagesReturned++ < 10)) 201 | { 202 | var postBody = issueAndPRQuery.ToJsonText(); 203 | var response = await client.Connection.Post(new Uri("https://api.github.com/graphql"), 204 | postBody, "application/json", "application/json"); 205 | 206 | JObject results = JObject.Parse(response.HttpResponse.Body.ToString()); 207 | 208 | hasMorePages = (bool)results["data"]["repository"]["issues"]["pageInfo"]["hasPreviousPage"]; 209 | issueAndPRQuery.Variables["start_cursor"] = results["data"]["repository"]["issues"]["pageInfo"]["startCursor"].ToString(); 210 | 211 | finalResults.Merge(results["data"]["repository"]["issues"]["nodes"]); 212 | } 213 | return finalResults; 214 | } 215 | 216 | private static string GetEnvVariable(string item, string error, string defaultValue) 217 | { 218 | var value = Environment.GetEnvironmentVariable(item); 219 | 220 | if (string.IsNullOrWhiteSpace(value)) 221 | { 222 | if (!string.IsNullOrWhiteSpace(error)) 223 | { 224 | Console.WriteLine(error); 225 | Environment.Exit(0); 226 | } 227 | 228 | if (!string.IsNullOrWhiteSpace(defaultValue)) 229 | { 230 | return defaultValue; 231 | } 232 | } 233 | 234 | return value; 235 | } 236 | 237 | class ProgressStatus : IProgress 238 | { 239 | readonly Action action; 240 | 241 | public ProgressStatus(Action progressAction) => 242 | action = progressAction; 243 | 244 | public void Report(int value) => action?.Invoke(value); 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /7 - Advanced Task Composition/README.md: -------------------------------------------------------------------------------- 1 | # Afternoon Async / Await lab 2 | 3 | ## Add error reporting. 4 | 5 | The first lab did nothing with errors. Let's fix that in two ways: 6 | 7 | 1. Validate args and state with a synchronous Task-returning method. 8 | 1. Report errors correctly with proper await or processing async errors. 9 | 10 | That means adding the API errors collection to the returned data. 11 | Throw when that data comes back bad. Each error has a message, locations array. 12 | 13 | ## Interface / API critique 14 | 15 | How would you create a class hierarchy for these APIs? What operations should be synchronous? What should be asynchronous? 16 | 17 | How does that shape construction, properties, and other elements of APIs? 18 | 19 | Some thoughts: 20 | Are async APIs factories that *produce* results as new objects? 21 | Should async APIs perform *actions* that modifying existing objects? 22 | 23 | ## Progress and cancellation. 24 | 25 | As a demo, show the page cursor features in the GitHub explorer web page. Once you've done that, introduce the lab: 26 | 27 | Now, we want all the open issues (newest first) in those respositories. Get them 25 per page. 28 | 29 | Add a progress report for "retrieved M of N issues". 30 | Support cancellation at the end of each response. 31 | 32 | If short of time, just add one overload that supports both cancellation and progress. 33 | If enough time, add all permutations. 34 | -------------------------------------------------------------------------------- /8 - State Machine Future/State Machine Future.pptx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8b30967f41af327a0535d054707add54617fd7c23ef574dc4f2d3676679efb0a 3 | size 797304 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Brandon Minnick 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 | # C# Workshop 2 | 3 | [NDC London 2019](https://ndc-london.com/workshop/become-a-better-c-programmer-more-value-more-expressions-no-waiting/) 4 | 5 | Hosted by [Bill Wagner](https://ndc-london.com/speaker/bill-wagner) 6 | 7 | ### Become a better C# programmer: more Value, more Expressions, no Waiting 8 | 9 | Over the past few releases, C# has added features that greatly improve productivity. 10 | 11 | In this workshop, you’ll learn scenarios where these new features make you more productive, and improve the clarity of your code. You’ll get a guided tour through the async and await wilderness. You'll start with basic uses where async and await work like magic. From there, you'll learn common practices and how async tasks compose. You'll dive into enough of the implementation details to understand how to apply async practices. After working through these different practices, you'll emerge from the wilderness understanding how to write clear, correct and safe async code. From there, you'll work through everyday scenarios where modern C# frees itself from the shackles of history. You'll see new ways to work with text, new expressions for control flow, and new ways to bend the type system to your will. 12 | You’ll learn: 13 | 14 | - [x] Getting started with the basics of async and await 15 | - [x] Distinguish asynchronous programming and parallel programming 16 | - [x] Compose asynchronous method calls throughout your code 17 | - [x] Understand the pitfalls of async void 18 | - [x] Designing async APIs 19 | - [x] Advanced async and Task based programming 20 | - [x] How string interpolation makes formatting strings much easier and clearer. 21 | - [x] How to create compound assignments and comparisons using Tuples and Deconstruction 22 | - [x] How to simplify iterators and async error handling using local functions 23 | - [x] How to simplify error reporting using throw expressions in expression bodied members. 24 | - [x] Write more performant code using value types safe pass-by-reference 25 | - [x] How to use Pattern Matching to create algorithms that extend existing types 26 | 27 | ## Computer Setup: 28 | 29 | Attendees will need to bring a laptop with one of the following setups: 30 | 31 | - Windows: Laptop running Visual Studio 2017, or Visual Studio Code 32 | - Mac: Laptop running Visual Studio Code 33 | - Linux: Laptop running Visual Studio Code 34 | 35 | ## Recommendations for Exercises 36 | 37 | 1. We're here to learn, not to win arguments. Some exercises will spark debate. *Listen and learn*. *what* answer your group finds is much less important than *why* you chose that answer. A related point is that you will learn from other groups' opinions, and teach by explaining your group's opinions. 38 | 2. Every person should be heard. 39 | 3. Change roles often in pair programming exercises. 40 | --------------------------------------------------------------------------------