Initial commit

This commit is contained in:
Mark J Price 2022-02-09 10:30:33 +00:00
parent 0e035df4e6
commit c7df0e3a90
56 changed files with 23256 additions and 2 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.DS_Store

View file

@ -1,2 +1,48 @@
# cs11dotnet7
Repository for the Packt Publishing book titled "C# 11 and .NET 7 - Modern Cross-Platform Development" by Mark J. Price
[![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/markjprice/cs11dotnet7)
[Improvements & Errata - list of corrections](errata.md)
# C# 11 and .NET 7 - Modern Cross-Platform Development, Seventh Edition
The previous edition was published on Tuesday, November 9, 2021. Order today from [Packt](https://www.packtpub.com/product/c-10-and-net-6-modern-cross-platform-development-sixth-edition/9781801077361), [Amazon.com](https://www.amazon.com/10-NET-Cross-Platform-Development-websites-dp-1801077363/dp/1801077363/), [Amazon.co.uk](https://www.amazon.co.uk/10-NET-Cross-Platform-Development-websites-dp-1801077363/dp/1801077363/), [Amazon.com.au](https://www.amazon.com.au/10-NET-Cross-Platform-Development-websites-ebook/dp/B09JV37DM6/), and all good book stores. My author page on Amazon: https://www.amazon.com/Mark-J-Price/e/B071DW3QGN/
## Code solutions for Visual Studio 2022 and Visual Studio Code
If you have Visual Studio 2022 for Windows and Visual Studio Code and its C# extension installed on the same computer, the build process can sometimes conflict. This is because Visual Studio has its own non-standard build server that is different from the standard build server used by .NET SDK. Visual Studio 2022 and Visual Studio Code also use different file types to group projects. So I have duplicated and separated solution code for each product:
- [/vs4win](/vs4win): Visual Studio 2022 for Windows solution files and projects for each chapter
- [/vscode](/vscode): Visual Studio Code workspace files and projects for each chapter
## Bonus content
The appendix is available to download as a PDF:
- Appendix A, Answers to the Test Your Knowledge Questions (coming November 2022)
Supplementary print book materials available to download:
- Color images of the screenshots/diagrams used in this book (coming November 2022)
## Important
Corrections for typos and other mistakes and improvements like refactoring code. Useful links to other related material.
- [Book Links](book-links.md)
- [Improvements & Errata - list of corrections](errata.md)
## Microsoft .NET community support
- .NET Developer Community: https://dotnet.microsoft.com/platform/community
- .NET Tech Community Forums for topic discussions: https://techcommunity.microsoft.com/t5/net/ct-p/dotnet
- Q&A for .NET to get your questions answered: https://docs.microsoft.com/en-us/answers/products/dotnet
- Technical questions about the C# programming language: https://docs.microsoft.com/en-us/answers/topics/dotnet-csharp.html
- If you'd like to apply to be a reviewer: https://authors.packtpub.com/reviewers/
## Interviews with me
Podcast interviews with me:
- [The .NET Core Podcast - February 4, 2022](https://dotnetcore.show/episode-91-c-sharp-10-and-dotnet-6-with-mark-j-price/)
- [The .NET Core Podcast - February 7, 2020](https://dotnetcore.show/episode-44-learning-net-core-with-mark-j-price/)
- [Yet Another Podcast with Jesse Liberty - May 2021](http://jesseliberty.com/2021/05/16/mark-price-on-c9-and-net-6/)
- [Yet Another Podcast with Jesse Liberty - February 2020](http://jesseliberty.com/2020/02/23/mark-price-c-net-core/)
- [Packt Podcasts](https://soundcloud.com/packt-podcasts/csharp-8-dotnet-core-3-the-evolution-of-the-microsoft-ecosystem)
Written interviews with me:
- [C# 9 and .NET 5: Book Review and Q&A](https://www.infoq.com/articles/book-interview-mark-price/?itm_source=infoq&itm_campaign=user_page&itm_medium=link)
- [Q&A with Episerver's Mark J. Price, author of C# 9 and .NET 5 - Modern Cross-Platform Development](https://www.episerver.com/articles/q-and-a-with-mark-price)
## Book cover
![C# 11 and .NET 7 by Packt Publishing](images/csdotnet-7e.png)

918
book-links.md Normal file
View file

@ -0,0 +1,918 @@
- [Chapter 1 - Hello, C#! Welcome, .NET!](#chapter-1---hello-c-welcome-net)
- [Visual Studio 2022 for Windows](#visual-studio-2022-for-windows)
- [Visual Studio Code](#visual-studio-code)
- [Other C# code editors and platforms](#other-c-code-editors-and-platforms)
- [.NET Interactive](#net-interactive)
- [Command Line Interfaces](#command-line-interfaces)
- [.NET](#net)
- [Open source and other projects related to .NET](#open-source-and-other-projects-related-to-net)
- [Git](#git)
- [Help and learning](#help-and-learning)
- [Chapter 2 - Speaking C](#chapter-2---speaking-c)
- [C# language version and the journey to C# 10](#c-language-version-and-the-journey-to-c-10)
- [C# language](#c-language)
- [C# implemented proposals](#c-implemented-proposals)
- [C# proposals being worked on](#c-proposals-being-worked-on)
- [Text types](#text-types)
- [Number types](#number-types)
- [Console apps and formatting](#console-apps-and-formatting)
- [Miscellaneous](#miscellaneous)
- [Chapter 3 - Controlling Flow and Converting Types](#chapter-3---controlling-flow-and-converting-types)
- [Operators](#operators)
- [Branching statements and pattern matching](#branching-statements-and-pattern-matching)
- [Casting, converting, rounding, and formatting data](#casting-converting-rounding-and-formatting-data)
- [Handling exceptions](#handling-exceptions)
- [Coding problems in interviews](#coding-problems-in-interviews)
- [Chapter 4 - Writing, Debugging, and Testing Functions](#chapter-4---writing-debugging-and-testing-functions)
- [Concepts](#concepts)
- [Debugging](#debugging)
- [Instrumenting](#instrumenting)
- [Testing](#testing)
- [Chapter 5 - Building Your Own Types with Object-Oriented Programming](#chapter-5---building-your-own-types-with-object-oriented-programming)
- [Fields](#fields)
- [Methods](#methods)
- [Properties](#properties)
- [Pattern matching](#pattern-matching)
- [Records](#records)
- [Miscellaneous](#miscellaneous-1)
- [Chapter 6 - Implementing Interfaces and Inheriting Classes](#chapter-6---implementing-interfaces-and-inheriting-classes)
- [Operator overloading](#operator-overloading)
- [Delegates and events](#delegates-and-events)
- [Interfaces and generics](#interfaces-and-generics)
- [Memory, reference and value types](#memory-reference-and-value-types)
- [Null and nullability](#null-and-nullability)
- [Inheritance](#inheritance)
- [Chapter 7 - Packaging and Distributing .NET Types](#chapter-7---packaging-and-distributing-net-types)
- [.NET Standard](#net-standard)
- [.NET versions](#net-versions)
- [.NET API, packages and frameworks](#net-api-packages-and-frameworks)
- [Roslyn, SDKs, and project templates](#roslyn-sdks-and-project-templates)
- [Packaging and publishing](#packaging-and-publishing)
- [Decompiling .NET](#decompiling-net)
- [Porting from .NET Framework to modern .NET](#porting-from-net-framework-to-modern-net)
- [Preview features](#preview-features)
- [Chapter 8 - Working with Common .NET Types](#chapter-8---working-with-common-net-types)
- [Working with numbers](#working-with-numbers)
- [Working with text and regular expressions](#working-with-text-and-regular-expressions)
- [Dates and times](#dates-and-times)
- [Collections](#collections)
- [Spans](#spans)
- [Networking](#networking)
- [Assemblies and reflection](#assemblies-and-reflection)
- [Working with images](#working-with-images)
- [Internationalization](#internationalization)
- [Chapter 9 - Working with Files, Streams, and Serialization](#chapter-9---working-with-files-streams-and-serialization)
- [File system, encoding, and streams](#file-system-encoding-and-streams)
- [Serialization](#serialization)
- [JSON](#json)
- [Chapter 10 - Working with Data Using Entity Framework Core](#chapter-10---working-with-data-using-entity-framework-core)
- [Legacy EF 6](#legacy-ef-6)
- [EF Core](#ef-core)
- [EF Core database providers](#ef-core-database-providers)
- [SQLite](#sqlite)
- [Microsoft SQL Server](#microsoft-sql-server)
- [NoSQL data stores](#nosql-data-stores)
- [EF Core models](#ef-core-models)
- [EF Core querying and manipulating](#ef-core-querying-and-manipulating)
- [Chapter 11 - Querying and Manipulating Data Using LINQ](#chapter-11---querying-and-manipulating-data-using-linq)
- [LINQ concepts](#linq-concepts)
- [LINQ providers](#linq-providers)
- [LINQ samples and tools](#linq-samples-and-tools)
- [Chapter 12 - Improving Performance and Scalability Using Multitasking](#chapter-12---improving-performance-and-scalability-using-multitasking)
- [Threads](#threads)
- [Tasks](#tasks)
- [Thread safety](#thread-safety)
- [async and await](#async-and-await)
- [Parallel programming](#parallel-programming)
- [Sharing resouces and synchronization](#sharing-resouces-and-synchronization)
- [Chapter 13 - Introducing Practical Applications of C# and .NET](#chapter-13---introducing-practical-applications-of-c-and-net)
- [.NET apps](#net-apps)
- [.NET Content Management Systems](#net-content-management-systems)
- [ASP.NET versions and features](#aspnet-versions-and-features)
- [Web development technologies](#web-development-technologies)
- [Windows desktop app development](#windows-desktop-app-development)
- [Third-party cross-platform GUI development](#third-party-cross-platform-gui-development)
- [Chapter 14 - Building Websites Using ASP.NET Core Razor Pages](#chapter-14---building-websites-using-aspnet-core-razor-pages)
- [General web development](#general-web-development)
- [ASP.NET Core](#aspnet-core)
- [Razor Pages and layouts](#razor-pages-and-layouts)
- [Endpoint routing](#endpoint-routing)
- [Chapter 15 - Building Websites Using the Model-View-Controller Pattern](#chapter-15---building-websites-using-the-model-view-controller-pattern)
- [Security and privacy](#security-and-privacy)
- [Setting up and configuring](#setting-up-and-configuring)
- [Controllers](#controllers)
- [Models](#models)
- [Views](#views)
- [Miscellaneous](#miscellaneous-2)
- [Chapter 16 - Building and Consuming Web Services](#chapter-16---building-and-consuming-web-services)
- [Web service technologies](#web-service-technologies)
- [Web service routing](#web-service-routing)
- [Web service clients](#web-service-clients)
- [Documenting web services](#documenting-web-services)
- [Securing web services](#securing-web-services)
- [Health checks and reliable web services](#health-checks-and-reliable-web-services)
- [Chapter 17 - Building User Interfaces Using Blazor](#chapter-17---building-user-interfaces-using-blazor)
- [Blazor hosting models](#blazor-hosting-models)
- [Blazor components](#blazor-components)
- [Advanced techniques](#advanced-techniques)
- [Other resources](#other-resources)
- [Chapter 18 - Building and Consuming Other Services](#chapter-18---building-and-consuming-other-services)
- [WCF](#wcf)
- [gRPC](#grpc)
- [SignalR](#signalr)
- [OData](#odata)
- [Azure Functions](#azure-functions)
- [Chapter 19 - Building Mobile and Desktop Apps Using .NET MAUI](#chapter-19---building-mobile-and-desktop-apps-using-net-maui)
- [.NET MAUI](#net-maui)
- [Mobile development](#mobile-development)
- [Chapter 20 - Protecting Your Data and Applications](#chapter-20---protecting-your-data-and-applications)
- [Cross-platform cryptography](#cross-platform-cryptography)
- [General security knowledge](#general-security-knowledge)
- [Encryption](#encryption)
- [Epilogue](#epilogue)
- [Next steps on your C# and .NET learning journey](#next-steps-on-your-c-and-net-learning-journey)
- [Learn from other Packt books](#learn-from-other-packt-books)
# Chapter 1 - Hello, C#! Welcome, .NET!
## Visual Studio 2022 for Windows
- Download Visual Studio for Windows: https://visualstudio.microsoft.com/downloads/
- Sign up for a Microsoft account: https://signup.live.com/
- Visual Studio for Windows documentation: https://docs.microsoft.com/en-us/visualstudio/windows/
- MSBuild and 64-bit Visual Studio 2022: https://devblogs.microsoft.com/dotnet/msbuild-and-64-bit-visual-studio-2022/
- Create C# apps with Visual Studio: https://docs.microsoft.com/en-us/visualstudio/get-started/csharp/
- Tutorial: Open a project from a repo: https://docs.microsoft.com/en-us/visualstudio/get-started/tutorial-open-project-from-repo-visual-studio-2019
- Comparison of Visual Studio Code and Visual Studio: https://www.itworld.com/article/3403683/visual-studio-code-stepping-on-visual-studios-toes.html
## Visual Studio Code
- Download Visual Studio Code: https://code.visualstudio.com/
- Set up Visual Studio Code: https://code.visualstudio.com/docs/setup/setup-overview
- Visual Studio Code key bindings and shortcuts: https://code.visualstudio.com/docs/getstarted/keybindings
- Windows: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-windows.pdf
- macOS: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf
- Linux: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-linux.pdf
- Latest versions of Visual Studio Code: https://code.visualstudio.com/updates
- Visual Studio Code documentation: https://code.visualstudio.com/docs
- Visual Studio Code user interface: https://code.visualstudio.com/docs/getstarted/userinterface
- Visual Studio Code support for C#: https://code.visualstudio.com/docs/languages/csharp
- Source code version control with Visual Studio Code: https://code.visualstudio.com/Docs/editor/versioncontrol
- Microsoft's plans for Visual Studio Code: https://github.com/Microsoft/vscode/wiki/Roadmap
- Learning with VS Code on Chromebooks: https://code.visualstudio.com/blogs/2020/12/03/chromebook-get-started
- Google and Amazon are supporters of Visual Studio Code: https://www.cnbc.com/2018/12/20/microsoft-cmo-capossela-says-google-employees-use-visual-studio-code.html
- Visual Studio Code: How Microsoft's 'any OS, any programming language, any software' plan is paying off: https://www.zdnet.com/article/visual-studio-code-
how-microsofts-any-os-any-programming-language-any-software-plan-is-paying-off/
- The History of Visual Studio Code: https://mybuild.microsoft.com/sessions/6b571733-8198-48da-b870-ef804dcfea93?source=sessions
## Other C# code editors and platforms
- Stack Overflow survey 2021 - Integrated development environment: https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-integrated-development-environment
- Stack Overflow survey 2019 - Most Popular Development Environments: https://insights.stackoverflow.com/survey/2019#development-environments-and-tools
- Visual Studio for Mac documentation: https://docs.microsoft.com/en-us/visualstudio/mac/
- GitHub Codespaces: https://docs.github.com/en/github/developing-online-with-codespaces/about-codespaces
- JetBrains Rider: https://www.jetbrains.com/rider/
- Rider documentation: https://www.jetbrains.com/help/rider/Introduction.html
- How to code with C# and .NET using a Raspberry Pi 400 with Ubuntu Desktop 64-bit by reading an extra article that I wrote at the following link: https://github.com/markjprice/cs9dotnet5-extras/blob/main/raspberry-pi-ubuntu64/README.md
## .NET Interactive
- .NET Interactive documentation: https://github.com/dotnet/interactive/tree/main/docs
- VS Code Notebooks: A Deep Dive video: https://www.youtube.com/watch?v=D-AXZZDTQhM
- Discuss: File format in the .NET Interactive VS Code extension: https://github.com/dotnet/interactive/issues/467
- Polyglot Notebooks: Variable Sharing: https://devblogs.microsoft.com/dotnet/net-interactive-preview-3-vs-code-insiders-and-polyglot-notebooks/
- Using HTML and JavaScript in .NET Interactive: https://github.com/dotnet/interactive/blob/main/docs/javascript-overview.md
- .NET Interactive Samples: https://github.com/dotnet/interactive/tree/main/samples
- Markdown syntax: https://daringfireball.net/projects/markdown/syntax
## Command Line Interfaces
- Windows Terminal as your Default Command Line Experience: https://devblogs.microsoft.com/commandline/windows-terminal-as-your-default-command-line-experience/
- .NET Core Command-Line Interface (CLI) tool: https://aka.ms/dotnet-cli-docs
## .NET
- Download .NET SDK: https://www.microsoft.com/net/download
- Stack Overflow survey 2021 - Most loved frameworks and libraries: https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-other-frameworks-and-libraries
- Themes of .NET: https://themesof.net/
- Microsoft's positioning of .NET Core and .NET Framework: https://devblogs.microsoft.com/dotnet/update-on-net-core-3-0-and-net-framework-4-8/
- Microsoft's plans for the journey to one .NET: https://devblogs.microsoft.com/dotnet/announcing-net-5-preview-4-and-our-journey-to-one-net/
- Official list of .NET 6 supported operating systems: https://github.com/dotnet/core/blob/master/release-notes/6.0/6.0-supported-os.md
- Windows ARM64 support: https://github.com/dotnet/runtime/issues/36699
- .NET Support Policy: https://dotnet.microsoft.com/platform/support/policy/dotnet-core
- .NET versions: https://docs.microsoft.com/en-us/dotnet/core/versions/
- .NET Uninstall Tool: https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool
- Remove .NET SDKs and runtimes: https://docs.microsoft.com/en-us/dotnet/core/install/remove-runtime-sdk-versions
- ASP.NET Core and EF Core branding for .NET 5: https://docs.microsoft.com/en-us/dotnet/core/dotnet-five
- .NET Optional SDK Workloads: https://github.com/dotnet/designs/blob/main/accepted/2020/workloads/workloads.md
- .NET Core runtime, CoreCLR: https://github.com/dotnet/runtime
- .NET Core Roadmap: https://github.com/dotnet/core/blob/master/roadmap.md
- .NET Standard FAQ: https://github.com/dotnet/standard/blob/master/docs/faq.md
- .NET Standard versions and which .NET platforms support them: https://github.com/dotnet/standard/blob/master/docs/versions.md
- .NET Team Members: https://twitter.com/i/lists/120961876//members
## Open source and other projects related to .NET
- Mono project: http://www.mono-project.com/
- Unity project: https://docs.unity3d.com/
- Open source projects to enable WCF and WF to migrate to .NET 5: https://devblogs.microsoft.com/dotnet/supporting-the-community-with-wf-and-wcf-oss-projects/
- Open source project for Blazor Web Forms components: https://github.com/FritzAndFriends/BlazorWebFormsComponents
## Git
- Download Git: https://git-scm.com/download
- Download GitHub Desktop: https://desktop.github.com
- Become a master at Git and Open Source: https://devblogs.microsoft.com/visualstudio/become-a-master-at-git-and-open-source/
## Help and learning
- Raise an issue with the book: https://github.com/markjprice/cs10dotnet6/issues
- .NET Conf 2021 Recap Videos, Slides, Demos, and More: https://devblogs.microsoft.com/dotnet/net-conf-2021-recap-videos-slides-demos-and-more/
- Microsoft Docs: https://docs.microsoft.com/
- Official .NET Blog written by the .NET engineering teams: https://devblogs.microsoft.com/dotnet/
- Scott Hanselman's videos: http://computerstufftheydidntteachyou.com/
- Get better at programming by learning how things work: https://jvns.ca/blog/learn-how-things-work/
- Practice .NET anywhere with .NET Fiddle: https://dotnetfiddle.net/
- Try .NET: https://try.dot.net/
- Stack Overflow: https://stackoverflow.com/
- Google Advanced Search: https://www.google.com/advanced_search
- Microsoft Learn: https://docs.microsoft.com/en-us/learn/
- .NET Videos: https://dotnet.microsoft.com/learn/videos
- Microsoft Channel 9 .NET Videos: https://channel9.msdn.com/Search?term=.net&lang-en=true
# Chapter 2 - Speaking C#
## C# language version and the journey to C# 10
- Welcome to C# 10: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
- Announcing Open Source C# standardization: https://devblogs.microsoft.com/dotnet/announcing-open-source-c-standardization-standards/
- Rosalyn and C# compiler versions: https://github.com/dotnet/roslyn/blob/master/docs/wiki/NuGet-packages.md
- How Microsoft rewrote its C# compiler in C# and made it open source: https://medium.com/microsoft-open-source-stories/how-microsoft-rewrote-its-c-compiler-in-c-and-made-it-open-source-4ebed5646f98
- Current status of the C# language: https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
- C# Language Specification 5.0: https://www.microsoft.com/en-us/download/details.aspx?id=7029
- Draft proposals for C# Language Specifications for 6.0 and later: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/
- What's new in C# 7.3: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3
- New C# 9.0 features: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9
- C# 9 Workshop: https://github.com/dotnet-presentations/csharp-workshop
- C# language versioning: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version
- The history of C#: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
- Interview with the C# Boss - Mads Torgersen: https://www.dotnetcurry.com/csharp/1455/mads-torgersen-interview
- The .NET Docs Show - C# Ask Me Anything 🤯: https://www.youtube.com/watch?v=U6cwOzUqjxY
- [EPIC] .NET 6 C# project templates use latest language idioms #3359: https://github.com/dotnet/templating/issues/3359
- SDK support for implicit namespaces in C# projects #25066: https://github.com/dotnet/docs/issues/25066
## C# language
- C# Reference: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/
- C# Programming Guide: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/
- C# Keywords: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/index
- Naming guidelines: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines
- Types (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/
- Statements, Expressions, and Operators (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/
## C# implemented proposals
- C# 10: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-10.0
- C# 9: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-9.0
- C# 8: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-8.0
- C# 7.3: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.3
- C# 7.2: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.2
- C# 7.1: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.1
- C# 7.0: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.0
- C# 6.0: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-6.0
## C# proposals being worked on
- [Proposal] Required Properties #3630: https://github.com/dotnet/csharplang/issues/3630
- Champion: Simplified parameter null validation code #2145: https://github.com/dotnet/csharplang/issues/2145
- Proposal: Semi-Auto-Properties; field keyword #140: https://github.com/dotnet/csharplang/issues/140
- Working Set (of proposals): https://github.com/dotnet/csharplang/milestone/19
## Text types
- Strings (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/
- Verbatim strings: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
- String Interpolation in C# 10 and .NET 6: https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/
- Escape sequences: https://devblogs.microsoft.com/csharpfaq/what-character-escape-sequences-are-available/
## Number types
- Floating point numbers: https://ciechanow.ski/exposing-floating-point/
- Why 0.1 does not exist in floating-point numbers: https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/
- American Patriot missile disaster: https://www.ima.umn.edu/~arnold/disasters/patriot.html
## Console apps and formatting
- Main() and command-line arguments (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/main-and-command-args/
- Console Class: https://docs.microsoft.com/en-us/dotnet/api/system.console
- Formatting types: https://docs.microsoft.com/en-us/dotnet/standard/base-types/formatting-types
- Composite Formatting: https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting
## Miscellaneous
- An Arabic programming language: https://youtu.be/dkO8cdwf6v8
# Chapter 3 - Controlling Flow and Converting Types
## Operators
- C# operators: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/operators
- Truth tables: https://en.wikipedia.org/wiki/Truth_table
- Side effects: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
- Bitwise and shift operators: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators
- Member access operators: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators
## Branching statements and pattern matching
- Statement keywords (C# Reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/statement-keywords
- Goto Fail bug: https://gotofail.com/
- goto keyword and examples of when it can be used: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/goto
- Pattern matching: https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching
- Patterns and switch expressions: https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/
## Casting, converting, rounding, and formatting data
- Taking control of rounding: https://docs.microsoft.com/en-us/dotnet/api/system.math.round
- Format codes for common scenarios: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
- Casting and type conversions (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/types/casting-and-type-conversions
## Handling exceptions
- 10 Exception handling best practices in C#: https://kumarashwinhubert.com/10-exception-handling-best-practices-in-csharp
## Coding problems in interviews
- Why can't programmers program? http://blog.codinghorror.com/why-cant-programmers-program/
- FizzBuzz for programming interviews: http://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/
- Make Better Hiring Decisions With Take-Home Coding Challenges: https://codesubmit.io
- The 30-minute guide to rocking your next coding interview: https://www.freecodecamp.org/news/coding-interviews-for-dummies-5e048933b82b/
- C# Interview Questions: https://www.codingdojo.com/blog/c-interview-questions
# Chapter 4 - Writing, Debugging, and Testing Functions
## Concepts
- Recursion versus interation: https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration
## Debugging
- Visual Studio for Windows debugger documentation: https://docs.microsoft.com/en-us/visualstudio/debugger/
- Visual Studio Code debugger: https://code.visualstudio.com/docs/editor/debugging
- Instructions for setting up the .NET debugger: https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md
- How to debug for absolute beginners: https://docs.microsoft.com/en-us/visualstudio/debugger/debugging-absolute-beginners
## Instrumenting
- System.Diagnostics Namespace: https://docs.microsoft.com/en-us/dotnet/core/api/system.diagnostics
- Debug class: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug
- Trace listeners: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracelistener
- Console log formatting: https://docs.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter
- Using .env in .NET: https://dusted.codes/dotenv-in-dotnet
- Announcing dotnet monitor in .NET 6: https://devblogs.microsoft.com/dotnet/announcing-dotnet-monitor-in-net-6/
## Testing
- Test Driven Development (TDD): https://en.wikipedia.org/wiki/Test-driven_development
- Unit testing in .NET: https://docs.microsoft.com/en-us/dotnet/core/testing/
- xUnit.net: http://xunit.github.io/
# Chapter 5 - Building Your Own Types with Object-Oriented Programming
## Fields
- Fields (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/fields
- Access modifiers (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/access-modifiers
- Enumeration types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum
## Methods
- Constructors (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/constructors
- Methods (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/methods
- Deconstruct method: https://docs.microsoft.com/en-us/dotnet/csharp/deconstruct
- params keyword: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params
- ref keyword: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/ref-returns
## Properties
- Encapsulation of fields using properties: https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors
- Properties (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/properties
- Using Tuples in C# to Initialize Properties in the Constructor and to Deconstruct Your Object: https://www.thomasclaudiushuber.com/2021/03/25/csharp-using-tuples-to-initialize-properties/
## Pattern matching
- Pattern matching tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/pattern-matching
## Records
- Comparing struct and class records: https://devblogs.microsoft.com/dotnet/announcing-net-6-release-candidate-2/#record-structs
## Miscellaneous
- How to calculate someone's age: https://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
# Chapter 6 - Implementing Interfaces and Inheriting Classes
## Operator overloading
- Operator (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading
- Symbols that can be overloaded: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators
## Delegates and events
- Delegates: https://docs.microsoft.com/en-us/dotnet/standard/delegates-lambdas
- Events (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/event
- Custom EventArgs: https://docs.microsoft.com/en-us/dotnet/standard/events/how-to-raise-and-consume-events
## Interfaces and generics
- Interfaces: https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces
- Explicit interface implementations: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation
- Design decisions for default interface implementations: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods
- Default interface implementations tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-members-versions
- Generics (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics
## Memory, reference and value types
- Reference Types (C# Reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
- Value Types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types
- Choosing Between Class and Struct: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct
- Conversation about the .NET type system: https://devblogs.microsoft.com/dotnet/conversation-about-the-net-type-system/
- Technical details of the internal memory layout of types in .NET: https://adamsitnik.com/Value-Types-vs-Reference-Types/
- Garbage collection: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/
- Finalizers (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/destructors
- Finalizers and disposing: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/unmanaged
- IDisposable interface: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/using-objects
## Null and nullability
- The inventor of null, Sir Charles Antony Richard Hoare, admits his mistake in a recorded hour-long talk: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
- The tweet about achieving 80% annotations in .NET 5: https://twitter.com/terrajobst/status/1296566363880742917
- Video to learn how to get rid of null reference exceptions forever: https://channel9.msdn.com/Shows/On-NET/This-is-how-you-get-rid-of-null-reference-exceptions-forever
- Nullable value types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types
- Nullable reference types: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
- Null-conditional operator: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators
- Null-coalescing operator: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
## Inheritance
- Inheritance (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/inheritance
# Chapter 7 - Packaging and Distributing .NET Types
## .NET Standard
- .NET Standard 2.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-standard-2-1/
- .NET Standard 2.1 APIs and a comparison with .NET Standard 2.0: https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1.md
- Future of .NET Standard: https://devblogs.microsoft.com/dotnet/the-future-of-net-standard/
- From .NET Standard to .NET 5: https://www.codemag.com/article/2010022
## .NET versions
- .NET Core 1.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-1-0/
- .NET Core 1.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-1-1/
- .NET Core 2.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-2-0/
- .NET Core 2.2 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-2-2/
- .NET Core 3.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0/
- .NET Core 3.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-3-1/
- .NET 5 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-5-0
- Performance improvements in .NET 5: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/
- .NET 5.0 Runtime Highlights: https://codemag.com/Article/2010092/.NET-5.0-Runtime-Highlights
- Announcing .NET 6 — The Fastest .NET Yet: https://devblogs.microsoft.com/dotnet/announcing-net-6/
## .NET API, packages and frameworks
- Search and browse all .NET APIs: https://docs.microsoft.com/en-us/dotnet/api/
- CoreFX README.md: https://github.com/dotnet/corefx/blob/master/Documentation/README.md
- .NET 6.0 TFMs: https://github.com/dotnet/designs/pull/174/files
- How packages and their APIs relate to frameworks: https://gist.github.com/davidfowl/8939f305567e1755412d6dc0b8baf1b7
- Example .NET package and the frameworks it supports: https://www.nuget.org/packages/System.IO.FileSystem/
- Currently supported Runtime Identifier (RID) values: https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog
- System.IO.FileSystem package details: https://www.nuget.org/packages/System.IO.FileSystem/
## Roslyn, SDKs, and project templates
- Roslyn compiler: https://github.com/dotnet/roslyn
- .NET project SDKs: https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview
- global.json overview: https://docs.microsoft.com/en-us/dotnet/core/tools/global-json
- Additional `dotnet new` templates: https://dotnetnew.azurewebsites.net/
- Tutorial: Create an item template: https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-item-template
## Packaging and publishing
- PDB files: https://www.wintellect.com/pdb-files-what-every-developer-must-know/
- Single-file app issue: https://github.com/dotnet/runtime/issues/36590
- App trimming: https://devblogs.microsoft.com/dotnet/app-trimming-in-net-5/
- The PackageReference format: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets
- NuGet packages: https://www.nuget.org/packages
- Announcing NuGet 6.0: https://devblogs.microsoft.com/nuget/announcing-nuget-6/
- .NET Core application publishing overview: https://docs.microsoft.com/en-us/dotnet/core/deploying/
- How to Deploy .NET apps to Raspberry Pi: https://www.ryadel.com/en/deploy-net-apps-raspberry-pi/
- How to solve a circular reference using an interface: https://stackoverflow.com/questions/6928387/how-to-solve-circular-reference
- Semantic versioning: http://semver.org
## Decompiling .NET
- Is it possible to “decompile” a Windows .exe? Or at least view the Assembly? https://stackoverflow.com/questions/273145/is-it-possible-to-decompile-a-windows-exe-or-at-least-view-the-assembly
- IDA Freeware Download Page: https://www.hex-rays.com/products/ida/support/download_freeware/
## Porting from .NET Framework to modern .NET
- Overview of porting from .NET Framework to .NET Core: https://docs.microsoft.com/en-us/dotnet/core/porting/
- What .NET Developers ought to know: https://www.hanselman.com/blog/WhatNETDevelopersOughtToKnowToStartIn2017.aspx
- Introducing the .NET Upgrade Assistant Preview: https://devblogs.microsoft.com/dotnet/introducing-the-net-upgrade-assistant-preview/
- .NET Upgrade Assistant: https://dotnet.microsoft.com/platform/upgrade-assistant
- Discover cross-platform issues: https://docs.microsoft.com/en-us/dotnet/standard/analyzers/api-analyzer#discover-cross-platform-issues
- Amazon Porting Assistant for .NET: https://aws.amazon.com/porting-assistant-dotnet/
## Preview features
- Preview Features: https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md
# Chapter 8 - Working with Common .NET Types
## Working with numbers
- Numerics types: https://docs.microsoft.com/en-us/dotnet/standard/numerics
- The Half type: https://devblogs.microsoft.com/dotnet/introducing-the-half-type/
- nint and nuint types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nint-nuint
- nint Struct: https://docs.microsoft.com/en-us/dotnet/api/system.nint
- Names for large numbers: https://en.wikipedia.org/wiki/Names_of_large_numbers
## Working with text and regular expressions
- String Class: https://docs.microsoft.com/en-us/dotnet/api/system.string
- Regex Class: https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex
- Regular expressions in .NET: https://docs.microsoft.com/en-us/dotnet/articles/standard/base-types/regular-expressions
- Regular Expression Language Quick Reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference
- Unicode in regular expressions: https://www.regular-expressions.info/unicode.html
- Using a regular expression to split a comma-separated string: https://stackoverflow.com/questions/18144431/regex-to-split-a-csv
- To specify a Unicode character, use `\u` followed by four characters specifying the number of the character. For example, `\u00c0` is the À character: https://www.regular-expressions.info
- Performance improvements in .NET 5: https://devblogs.microsoft.com/dotnet/regex-performance-improvements-in-net-5/
## Dates and times
- Date, Time, and Time Zone Enhancements in .NET 6: https://devblogs.microsoft.com/dotnet/date-time-and-time-zone-enhancements-in-net-6/
- .NET 6: Date and Time Structures: https://www.infoq.com/news/2021/04/Net6-Date-Time/
- Noda Time: A better date and time API for .NET: https://nodatime.org/
## Collections
- Collections (C# and Visual Basic): https://docs.microsoft.com/en-us/dotnet/api/system.collections
- Collections: https://docs.microsoft.com/en-us/dotnet/standard/collections
- Concurrent collections: https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent
- What types should I use to pass collections in C#? https://markheath.net/post/passing-collections-csharp
- On Abstractions and For-Each Performance in C#: https://www.infoq.com/articles/For-Each-Performance/
## Spans
- Span<T>: https://docs.microsoft.com/en-us/dotnet/api/system.span-1
- How spans work internally: https://docs.microsoft.com/en-us/archive/msdn-magazine/2018/january/csharp-all-about-span-exploring-a-new-net-mainstay
## Networking
- .NET 6 Networking Improvements: https://devblogs.microsoft.com/dotnet/dotnet-6-networking-improvements/
## Assemblies and reflection
- .NET API Reference: https://docs.microsoft.com/en-us/dotnet/api/
- Compiler-generated display class: http://stackoverflow.com/a/2509524/55847
- Dynamically load assemblies that are not currently referenced: https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability-howto
- Dynamically execute code: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke
- Dynamically generate new code and assemblies: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.assemblybuilder
- Extending Metadata Using Attributes: https://docs.microsoft.com/en-us/dotnet/standard/attributes/
## Working with images
- ImageSharp: https://github.com/SixLabors/ImageSharp
## Internationalization
- Globalizing and localizing .NET applications: https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/
- Time zones: https://devblogs.microsoft.com/dotnet/cross-platform-time-zones-with-net-core/
# Chapter 9 - Working with Files, Streams, and Serialization
## File system, encoding, and streams
- File System and the Registry (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/
- Character encoding in .NET: https://docs.microsoft.com/en-us/dotnet/articles/standard/base-types/character-encoding
- Async streams tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/generate-consume-asynchronous-stream
- How pipelines help with I/O performance: https://devblogs.microsoft.com/dotnet/system-io-pipelines-high-performance-io-in-net/
## Serialization
- Serialization (C#): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/concepts/serialization/
- Serializing to Files, TextWriters, and XmlWriters: https://docs.microsoft.com/en-us/dotnet/standard/linq/serialize-files-textwriters-xmlwriters
## JSON
- Newtonsoft Json.NET: https://www.newtonsoft.com/json
- System.Text.Json APIs: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
- Issues solved by the new JSON APIs, including JamesNK's comments: https://github.com/dotnet/corefx/issues/33115
- How to migrate from Newtonsoft.Json to System.Text.Json: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to
- Whats next for System.Text.Json? https://devblogs.microsoft.com/dotnet/whats-next-for-system-text-json/
# Chapter 10 - Working with Data Using Entity Framework Core
## Legacy EF 6
- Entity Framework 6.3 and its .NET Core 3.0 and later support: https://devblogs.microsoft.com/dotnet/announcing-ef-core-3-0-and-ef-6-3-general-availability/
## EF Core
- EF Core documentation: https://docs.microsoft.com/en-us/ef/core/
- EF Core team's plans: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/plan
- New features in EF Core 5: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew
- New features in EF Core 6: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-6.0/whatsnew
- Get to Know EF Core 6: https://devblogs.microsoft.com/dotnet/get-to-know-ef-core-6/
- Announcing the Plan for EF7: https://devblogs.microsoft.com/dotnet/announcing-the-plan-for-ef7/
- Plans for Entity Framework Core 6.0 revealed as Microsoft admits it is unlikely to match Dapper for performance: https://www.theregister.com/2021/01/19/entity_framework_core_6/
- Entity Framework Community Standup - Performance Tuning an EF Core App: https://www.youtube.com/watch?v=VgNFFEqwZPU
## EF Core database providers
- EF Core database providers: https://docs.microsoft.com/en-us/ef/core/providers/
- Devart database providers: https://www.devart.com/dotconnect/entityframework.html
- Check the latest NuGet package version: https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/
- Document Database Providers for Entity Framework Core: https://github.com/BlueshiftSoftware/EntityFrameworkCore
## SQLite
- Download SQLite: https://www.sqlite.org/download.html
- SQL statements supported by SQLite: https://sqlite.org/lang.html
- Download SQLiteStudio: http://sqlitestudio.pl
## Microsoft SQL Server
- Try SQL Server on-premises or in the cloud: https://www.microsoft.com/en-us/sql-server/sql-server-downloads
- Download SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms
- Quickstart: Connect and query a SQL Server instance using SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/quickstarts/ssms-connect-query-sql-server
- Quickstart: Connect and query an Azure SQL Database or an Azure Managed Instance using SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/quickstarts/ssms-connect-query-azure-sql
- Use Visual Studio Code to create and run Transact-SQL scripts: https://docs.microsoft.com/en-us/sql/tools/visual-studio-code/sql-server-develop-use-vscode
- .NET Interactive with SQL!| .NET Notebooks in Visual Studio Code: https://devblogs.microsoft.com/dotnet/net-interactive-with-sql-net-notebooks-in-visual-studio-code/
## NoSQL data stores
- Welcome to Azure Cosmos DB: https://docs.microsoft.com/en-us/azure/cosmos-db/introduction
- Use NoSQL databases as a persistence infrastructure: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure
## EF Core models
- EF Core model conventions: https://docs.microsoft.com/en-us/ef/core/modeling/
- Data seeding: https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding
- Humanizer library: http://humanizr.net
- Scaffolding: https://docs.microsoft.com/en-us/ef/core/managing-schemas/scaffolding?tabs=dotnet-core-cli
- Deep Dive into Many-to-Many: A Tour of EF Core 5.0: https://channel9.msdn.com/Shows/On-NET/Deep-Dive-into-Many-to-Many-A-Tour-of-EF-Core-50-pt-2
- Naming Conventions for Entity Framework Core Tables and Columns: https://github.com/efcore/EFCore.NamingConventions
## EF Core querying and manipulating
- Filtered include: https://docs.microsoft.com/en-us/ef/core/querying/related-data/eager#filtered-include
- Query tags: https://docs.microsoft.com/en-us/ef/core/querying/tags
- Loading patterns: https://docs.microsoft.com/en-us/ef/core/querying/related-data
- Pooling database contexts: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#dbcontext-pooling
# Chapter 11 - Querying and Manipulating Data Using LINQ
## LINQ concepts
- Expression trees: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/
- LINQ comprehension syntax: https://stackoverflow.com/questions/6229187/linq-why-is-it-called-comprehension-syntax
- LINQ in C#: https://docs.microsoft.com/en-us/dotnet/csharp/linq/linq-in-csharp
- New LINQ extensions in .NET 6 and benchmarks: https://blog.elmah.io/new-linq-extensions-in-net-6-and-benchmarks/
## LINQ providers
- EF Core LINQ queries no longer evaluated at client: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
- Parallel LINQ (PLINQ): https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq
- LINQ to XML Overview (C#): https://docs.microsoft.com/en-gb/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-overview
## LINQ samples and tools
- 101 LINQ Samples: https://docs.microsoft.com/en-us/samples/dotnet/try-samples/101-linq-samples/
- LINQPad: https://www.linqpad.net/
# Chapter 12 - Improving Performance and Scalability Using Multitasking
## Threads
- Threads and threading: https://docs.microsoft.com/en-us/dotnet/standard/threading/threads-and-threading
- Thread pool: https://docs.microsoft.com/en-us/dotnet/standard/threading/the-managed-thread-pool
## Tasks
- Pros and cons of different ways to start tasks: https://devblogs.microsoft.com/pfxteam/task-factory-startnew-vs-new-task-start/
- Task.Start Method: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.start
## Thread safety
- Events and thread-safety: https://docs.microsoft.com/en-us/archive/blogs/cburrows/field-like-events-considered-harmful
- Stephen Cleary's thoughts on events and thread-safety: https://blog.stephencleary.com/2009/06/threadsafe-events.html
- Managed threading best practices: https://docs.microsoft.com/en-us/dotnet/standard/threading/managed-threading-best-practices
- ThreadStaticAttribute Class: https://docs.microsoft.com/en-us/dotnet/api/system.threadstaticattribute
## async and await
- Async in depth: https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth
- await (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await
- Six Essential Tips for Async: https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async
- Exploring the async/await State Machine The Awaitable Pattern: https://vkontech.com/exploring-the-async-await-state-machine-the-awaitable-pattern/
- There Is No Thread: https://blog.stephencleary.com/2013/11/there-is-no-thread.html
## Parallel programming
- Parallel Programming in .NET: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/
## Sharing resouces and synchronization
- Overview of synchronization primitives: https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives
# Chapter 13 - Introducing Practical Applications of C# and .NET
## .NET apps
- .NET Application Architecture Guidance: https://www.microsoft.com/net/learn/architecture
## .NET Content Management Systems
- Piranha CMS: https://piranhacms.org/
- Orchard Core: http://www.orchardcore.net/
- Optimizely CMS Developer Guide: https://world.optimizely.com/documentation/developer-guides/CMS/
- Umbraco CMS: https://umbraco.com/products/umbraco-cms/migrating-umbraco-to-net-core/
## ASP.NET versions and features
- ASP.NET Core 1.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-1-0/
- ASP.NET Core 1.1 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-1-1/
- ASP.NET Core 2.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-2-0/
- ASP.NET Core 2.1 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-2-1-0-now-available/
- ASP.NET Core 2.2 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-2-2-available-today/
- ASP.NET Core 3.0 announcement: https://devblogs.microsoft.com/aspnet/a-first-look-at-changes-coming-in-asp-net-core-3-0/
- ASP.NET Core 3.1 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-1/
- Blazor WebAssembly announcement: https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-now-available/
- ASP.NET Core 5.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-in-net-5/
## Web development technologies
- WebSocket: https://en.wikipedia.org/wiki/WebSocket
- MessagePack: https://msgpack.org
- WebAssembly: https://webassembly.org
- TypeScript: https://www.typescriptlang.org
## Windows desktop app development
- Choices of platform for building Windows desktop apps: https://docs.microsoft.com/en-us/windows/apps/desktop/choose-your-platform
- Whats new in Windows Forms in .NET 6.0: https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-in-net-6-0/
- Migrating WPF apps: https://devblogs.microsoft.com/dotnet/migrating-a-sample-wpf-app-to-net-core-3-part-1/
- Windows Compatibility Pack: https://devblogs.microsoft.com/dotnet/announcing-the-windows-compatibility-pack-for-net-core/
- Packt book about building WPF apps: https://www.packtpub.com/product/mastering-windows-presentation-foundation/9781785883002
- UWP apps: https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide
- Nine desktop development options, from WPF to Blazor: https://visualstudiomagazine.com/articles/2021/03/25/desktop-options.aspx
## Third-party cross-platform GUI development
- avalonia - A cross platform XAML framework for .NET: https://avaloniaui.net
- Uno Platform: https://platform.uno
- Getting Started with the Uno Platform (Project Reunion/WinUI): https://nicksnettravels.builttoroam.com/getting-started-uno-platform/
# Chapter 14 - Building Websites Using ASP.NET Core Razor Pages
## General web development
- HTML5 and CSS3: Building Responsive Websites: https://www.packtpub.com/producthtml5-and-css3-building-responsive-websites/9781787124813h
- Kestrel web server: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel
- Free TLS/SSL certificates: https://letsencrypt.org
- ASP.NET Core hosting environments: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments
- Content Delivery Network (CDN): https://en.wikipedia.org/wiki/Content_delivery_network
- Bootstrap: https://getbootstrap.com/
- Compact Folders feature: https://github.com/microsoft/vscode-docs/blob/vnext/release-notes/v1_41.md#compact-folders-in-explorer
## ASP.NET Core
- Announcing ASP.NET Core in .NET 6: https://devblogs.microsoft.com/dotnet/announcing-asp-net-core-in-net-6/
- ASP.NET Core fundamentals: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/
- Static files in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files
- HttpContext class: https://docs.microsoft.com/en-us/dotnet/api/system.web.httpcontext
## Razor Pages and layouts
- Introducing the new Razor editor in Visual Studio 2022: https://devblogs.microsoft.com/visualstudio/introducing-the-new-razor-editor-in-visual-studio-2022/
- Introduction to Razor Pages in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/
- Razor syntax reference for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor
- Layout in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout
- Tag Helpers in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro
- ASP.NET Core Razor Pages with EF Core: https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro
- Registering a database context for use as a dependency service: https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#using-dbcontext-with-dependency-injection
- The `<partial>` tag helper: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper
## Endpoint routing
- Simple examples of Run, Map, and Use: https://www.vaughanreid.com/2020/05/using-in-line-middleware-in-asp-net-core/
- Automatically visualize your endpoints: https://andrewlock.net/visualizing-asp-net-core-endpoints-using-graphvizonline-and-the-dot-language/
- Configuring the HTTP pipeline with middleware: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware
- DEEP DIVE: HOW IS THE ASP.NET CORE MIDDLEWARE PIPELINE BUILT? https://www.stevejgordon.co.uk/how-is-the-asp-net-core-middleware-pipeline-built
# Chapter 15 - Building Websites Using the Model-View-Controller Pattern
## Security and privacy
- Built-in features for compliance with modern privacy requirements like GDPR: https://docs.microsoft.com/en-us/aspnet/core/security/gdpr
- ASP.NET Core's support for authenticator apps: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-enable-qrcodes
- Identity UI library: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?tabs=netcore-cli
- Authorization: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction
- Anti-forgery tokens: https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
## Setting up and configuring
- dotnet new templates: https://github.com/dotnet/templating/wiki/Available-templates-for-dotnet-new
- Overview of ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/overview
- Default configuration of web hosts: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/web-host
- Dependency injection for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
- Configuring middleware: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/
- Announcing YARP 1.0 Release: https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release/
## Controllers
- Handle requests with controllers in ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions
- Response caching: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response
- How cache busting using query strings works: https://stackoverflow.com/questions/9692665/cache-busting-via-params
## Models
- Model Binding in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
- Create your own model binders by implementing the IModelBinder interface: https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding
- Model validation: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation
- Tutorial: Get started with EF Core in an ASP.NET MVC web app: https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
## Views
- HtmlHelper class: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.viewfeatures.htmlhelper
- Views in ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview
- Why it is good to put <script> elements at the bottom of the <body>: https://stackoverflow.com/questions/436411/where-should-i-put-script-tags-in-html-markup
## Miscellaneous
- How to unit test controllers: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/testing
- Filters for cross-concern functionality: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters
- Free stock photos for commercial use with no attribution: https://www.pexels.com/
- Images of eight categories: https://github.com/markjprice/cs9dotnet5/tree/master/Assets/Categories
# Chapter 16 - Building and Consuming Web Services
## Web service technologies
- Media types: http://en.wikipedia.org/wiki/Media_type
- WS-* standards: https://en.wikipedia.org/wiki/List_of_web_service_specifications
- HTTP OPTIONS method and other HTTP methods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
- HTTP POST requests: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
- Create web APIs with ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/web-api/
## Web service routing
- Design decisions around endpoint routing: https://devblogs.microsoft.com/aspnet/asp-net-core-2-2-0-preview1-endpoint-routing/
- Endpoint routing: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing
- Previous routing system: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1
- Route constraints: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing#route-constraint-reference
- Dependency injection for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
- Proposed standard for Problem Details for HTTP APIs: https://tools.ietf.org/html/rfc7807
- Implementing problem details: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails
## Web service clients
- REST Client: https://github.com/Huachao/vscode-restclient/blob/master/README.md
- It is the BaseAddress and DefaultRequestHeaders properties that you should treat with caution with multiple threads: https://medium.com/@nuno.caneco/c-httpclient-should-not-be-disposed-or-should-it-45d2a8f568bc
- You're using HttpClient wrong and it is destabilizing your software: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
- How to initiate HTTP requests: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
- Issues with the original HttpClient class available in .NET: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net
- HttpClient extension methods for easily working with JSON: https://github.com/dotnet/designs/blob/main/accepted/2020/json-http-extensions/json-http-extensions.md
## Documenting web services
- Swagger: https://swagger.io/
- Swagger Tools: https://swagger.io/tools/
- Swashbuckle for ASP.NET Core: https://github.com/domaindrivendev/Swashbuckle.AspNetCore
- How Swagger can support multiple versions of an API: https://stackoverflow.com/questions/30789045/leverage-multipleapiversions-in-swagger-with-attribute-versioning/30789944
- Importance of documenting services: https://idratherbewriting.com/learnapidoc/
- Benefits of setting version compatibility: https://docs.microsoft.com/en-us/aspnet/core/mvc/compatibility-version
- Check latest version of analyzers package: http://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Api.Analyzers/
## Securing web services
- Verifying that the tokens used to call your web APIs are requested with the expected claims: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-verification-scope-app-roles
- CORS can be enabled to allow different origin requests: https://docs.microsoft.com/en-us/aspnet/core/security/cors
- Common HTTP security headers that you might want to add: https://www.meziantou.net/security-headers-in-asp-net-core.htm
## Health checks and reliable web services
- Health checks in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks
- How to extend the health check response: https://blogs.msdn.microsoft.com/webdev/2018/08/22/asp-net-core-2-2-0-preview1-healthcheck/
- How Polly can make your web services more reliable: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly
- Use HttpClientFactory to implement resilient HTTP requests: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
- Redis: https://redis.io
# Chapter 17 - Building User Interfaces Using Blazor
## Blazor hosting models
- Blazor: https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor
- Official list of supported Blazor platforms: https://docs.microsoft.com/en-us/aspnet/core/blazor/supported-platforms
- Blazor hosting models: https://docs.microsoft.com/en-us/aspnet/core/blazor/hosting-models
- Blazor Mobile Bindings: https://devblogs.microsoft.com/aspnet/mobile-blazor-bindings-experiment/
- Blazor Hybrid apps: https://devblogs.microsoft.com/aspnet/hybrid-blazor-apps-in-mobile-blazor-bindings-july-update/
## Blazor components
- The reason for needing CSS isolation for Blazor components: https://github.com/dotnet/aspnetcore/issues/10170
- OI icons: https://iconify.design/icon-sets/oi/
- Setting `<head>` elements: https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/additional-scenarios-influence-html-head-tag-elements
- Forms and validation: https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation
- NavigationManager with Blazor routes: https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing#uri-and-navigation-state-helpers
## Advanced techniques
- Implementing offline support for Blazor WebAssembly projects: https://docs.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app#offline-support
- Lazy loading assemblies: https://docs.microsoft.com/en-us/aspnet/core/blazor/webassembly-lazy-load-assemblies?view=aspnetcore-5.0
- Routing in Blazor Apps: Comparing the routing of popular web frameworks like React and Angular with Blazor: https://devblogs.microsoft.com/premier-developer/routing-in-blazor-apps/
## Other resources
- Awesome Blazor: A collection of awesome Blazor resources: https://github.com/AdrienTorris/awesome-blazor
- Blazor University: Learn the new .NET SPA framework from Microsoft: https://blazor-university.com
- Blazor - app building workshop: In this workshop, we will build a complete Blazor app and learn about the various Blazor framework features along the way: https://github.com/dotnet-presentations/blazor-workshop/
- Carl Franklin's Blazor Train: https://www.youtube.com/playlist?list=PL8h4jt35t1wjvwFnvcB2LlYL4jLRzRmoz
- Welcome to PACMAN written in C# and running on Blazor WebAssembly: https://github.com/SteveDunn/PacManBlazor
# Chapter 18 - Building and Consuming Other Services
## WCF
- Core WCF repository: https://github.com/CoreWCF/CoreWCF
## gRPC
- gRPC: https://grpc.io
- Whats new for gRPC in .NET 6: https://devblogs.microsoft.com/dotnet/grpc-in-dotnet-6/
- Introduction to gRPC on .NET: https://docs.microsoft.com/en-us/aspnet/core/grpc/
- gRPC as an alternative to WCF: https://devblogs.microsoft.com/premier-developer/grpc-asp-net-core-as-a-migration-path-for-wcfs-in-net-core/
- How to use gRPC with ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/grpc/aspnetcore
## SignalR
- SignalR: https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction
## OData
- OData - the best way to REST: https://www.odata.org
- OData documentation: https://docs.microsoft.com/en-us/odata/
## Azure Functions
- Azure Functions documentation: https://docs.microsoft.com/en-us/azure/azure-functions/
# Chapter 19 - Building Mobile and Desktop Apps Using .NET MAUI
## .NET MAUI
- Introduction to .NET MAUI: https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/
- GitHub repository for .NET MAUI: https://github.com/dotnet/maui
## Mobile development
- If you want to use Visual Studio 2019 to create a mobile app, then you can read how to connect to a Mac build host: https://docs.microsoft.com/en-us/xamarin/ios/get-started/installation/windows/connecting-to-mac/
- Pros and cons of the two major mobile platforms based on aspects such as revenue generation and user engagement: https://fueled.com/blog/app-store-vs-google-play/
- ATS: https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/ats
- Handling self-signed certificates: https://docs.remotingsdk.com/Clients/Tasks/HandlingSelfSignedCertificates/NET/
- Android and cleartext support: https://devblogs.microsoft.com/xamarin/cleartext-http-android-network-security/
- Self Signed iOS Certifcates and Certificate Pinning in a Xamarin.Forms application: https://nicksnettravels.builttoroam.com/ios-certificate/
- Protecting your users with certificate pinning: https://www.basdecort.com/protecting-your-users-with-certificate-pinning/
- HttpClient and SSL/TLS implementation selector for iOS/macOS: https://docs.microsoft.com/en-us/xamarin/cross-platform/macios/http-stack
# Chapter 20 - Protecting Your Data and Applications
## Cross-platform cryptography
- Features supported by which OS: https://docs.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography
## General security knowledge
- Key Security Concepts: https://docs.microsoft.com/en-us/dotnet/standard/security/key-security-concepts
- Dictionary Attacks 101: https://blog.codinghorror.com/dictionary-attacks-101/
- The first publicly known SHA1 collision happened in 2017: https://arstechnica.co.uk/information-technology/2017/02/at-deaths-door-for-years-widely-used-sha1-function-is-now-dead/
## Encryption
- The RSA algorithm is based on the factorization of large integers: http://mathworld.wolfram.com/RSAEncryption.html
- Encrypting Data: https://docs.microsoft.com/en-us/dotnet/standard/security/encrypting-data
- Cryptographic Signatures: https://docs.microsoft.com/en-us/dotnet/standard/security/cryptographic-signatures
# Epilogue
## Next steps on your C# and .NET learning journey
- Follow the Framework Design Guidelines: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
- Follow the code style rules: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
- Cloud-Native learning resources for .NET developers: https://devblogs.microsoft.com/dotnet/cloud-native-learning-resources-for-net-developers/
- 37 App Ideas for Bootcamp Students & Code Newbies: https://dev.to/sylwiavargas/33-app-ideas-for-bootcamp-students-code-newbies-3n28
- Just! Build! Websites! https://github.com/melanierichards/just-build-websites
## Learn from other Packt books
- Mobile Development with .NET: https://www.packtpub.com/product/mobile-development-with-net-second-edition/9781800204690
- Enterprise Application Development with C# 9 and .NET 5: https://www.packtpub.com/product/enterprise-application-development-with-c-9-and-net-5/9781800209442
- Software Architecture with C# 9 and .NET 5: https://www.packtpub.com/product/software-architecture-with-c-9-and-net-5-second-edition/9781800566040
- An Atypical ASP.NET Core 5 Design Patterns Guide: https://www.packtpub.com/product/an-atypical-asp-net-core-5-design-patterns-guide/9781789346091
- Customizing ASP.NET Core 5.0: https://www.packtpub.com/product/customizing-asp-net-core-5-0/9781801077866
- ASP.NET Core Secure Coding Cookbook: https://www.packtpub.com/product/asp-net-core-secure-coding-cookbook/9781801071567
- ASP.NET Core 5 and React: https://www.packtpub.com/product/asp-net-core-5-and-react-second-edition/9781800206168
- ASP.NET Core 5 and Angular: https://www.packtpub.com/product/asp-net-core-5-and-angular-fourth-edition/9781800560338
- ASP.NET Core and Vue.js: https://www.packtpub.com/product/asp-net-core-and-vue-js/9781800206694
- Practical Microservices with Dapr and .NET: https://www.packtpub.com/product/practical-microservices-with-dapr-and-net/9781800568372
- Web Development with Blazor and .NET 5: https://www.packtpub.com/product/web-development-with-blazor-and-net-5/9781800208728
- Mastering Windows Presentation Foundation: https://www.packtpub.com/product/mastering-windows-presentation-foundation-second-edition/9781838643416

12
errata.md Normal file
View file

@ -0,0 +1,12 @@
# Errata, Improvements and Troubleshooting
If you find any mistakes in the sixth edition, *C# 11 and .NET 7 - Modern Cross-Platform Development*, or if you have suggestions for improvements, then please [raise an issue in this repository](https://github.com/markjprice/cs11dotnet7/issues) or email me at markjprice (at) gmail.com.
- [Errata, Improvements and Troubleshooting](#errata-improvements-and-troubleshooting)
- [Print Book](#print-book)
- [Bonus Content](#bonus-content)
# Print Book
# Bonus Content

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
images/csdotnet-7e.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

20
notebooks/Chapter01.dib Normal file
View file

@ -0,0 +1,20 @@
#!markdown
# Chapter 1 - Hello, C#! Welcome, .NET!
Mixing *rich* **text** and code is cool!
#!csharp
#!about
#!csharp
Console.WriteLine("Hello, .NET Interactive!")
#!csharp
var number = 8;
#!csharp
Console.WriteLine(number);

473
notebooks/Chapter02.dib Normal file
View file

@ -0,0 +1,473 @@
#!csharp
#!markdown
# Chapter 2 - Speaking C#
#!csharp
using static System.Console;
#!markdown
## Showing the compiler version
#!csharp
// #error version
#!markdown
## Help for writing correct code
In a .NET Interactive notebook, you do not need to end the last statement with a semi-colon.
#!csharp
// with two compile errors
Console.Writeline("Hello World!")
// fixed
Console.WriteLine("Hello World!")
#!markdown
## Revealing the extent of the C# vocabulary
When running this code inside a .NET Interactive notebook, it lists all possible assemblies, including some used by the extension like `Microsoft.DotNet.Interactive`.
#!csharp
using System.Linq;
using System.Reflection;
// loop through the assemblies that this app references
foreach (var r in Assembly.GetEntryAssembly()
.GetReferencedAssemblies())
{
// load the assembly so we can read its details
var a = Assembly.Load(new AssemblyName(r.FullName));
// declare a variable to count the number of methods
int methodCount = 0;
// loop through all the types in the assembly
foreach (var t in a.DefinedTypes)
{
// add up the counts of methods
methodCount += t.GetMethods().Count();
}
// output the count of types and their methods
Console.WriteLine(
"{0:N0} types with {1:N0} methods in {2} assembly.",
arg0: a.DefinedTypes.Count(),
arg1: methodCount,
arg2: r.Name);
}
#!markdown
## Naming things and assigning values
#!csharp
// let the heightInMetres variable become equal to the value 1.88
double heightInMetres = 1.88;
Console.WriteLine($"The variable {nameof(heightInMetres)} has the value {heightInMetres}.");
#!markdown
## Storing text
#!csharp
char letter = 'A'; // assigning literal characters
char digit = '1';
char symbol = '$';
char userChoice = GetSomeKeystroke(); // assigning from a fictitious function
string firstName = "Bob"; // assigning literal strings
string lastName = "Smith";
string phoneNumber = "(215) 555-4256";
// assigning a string returned from a fictitious function
string address = GetAddressFromDatabase(id: 563);
#!markdown
## Understanding verbatim strings
In a .NET Interactive notebook, a brighter red color is used for escaped characters inside a string value to make them easier to see.
`\t` means tab. `\b` means backspace. `\s` does is not a valid escape character.
#!csharp
string fullNameWithTabSeparator = "Bob\tSmith";
string filePath = "C:\televisions\sony\bravia.txt";
#!csharp
string filePath = @"C:\televisions\sony\bravia.txt";
#!markdown
## Storing numbers
#!csharp
// unsigned integer means positive whole number or 0
uint naturalNumber = 23;
// integer means negative or positive whole number or 0
int integerNumber = -23;
// float means single-precision floating point
// F suffix makes it a float literal
float realNumber = 2.3F;
// double means double-precision floating point
double anotherRealNumber = 2.3; // double literal
#!markdown
## Storing whole numbers
#!csharp
// three variables that store the number 2 million
int decimalNotation = 2_000_000;
int binaryNotation = 0b_0001_1110_1000_0100_1000_0000;
int hexadecimalNotation = 0x_001E_8480;
// check the three variables have the same value
// both statements output true
Console.WriteLine($"{decimalNotation == binaryNotation}");
Console.WriteLine(
$"{decimalNotation == hexadecimalNotation}");
#!markdown
## Writing code to explore number sizes
#!csharp
Console.WriteLine($"int uses {sizeof(int)} bytes and can store numbers in the range {int.MinValue:N0} to {int.MaxValue:N0}.");
Console.WriteLine($"double uses {sizeof(double)} bytes and can store numbers in the range {double.MinValue:N0} to {double.MaxValue:N0}.");
Console.WriteLine($"decimal uses {sizeof(decimal)} bytes and can store numbers in the range {decimal.MinValue:N0} to {decimal.MaxValue:N0}.");
#!markdown
## Comparing double and decimal
#!csharp
Console.WriteLine("Using doubles:");
double a = 0.1;
double b = 0.2;
if (a + b == 0.3)
{
Console.WriteLine($"{a} + {b} equals 0.3");
}
else
{
Console.WriteLine($"{a} + {b} does NOT equal 0.3");
}
#!csharp
Console.WriteLine("Using decimals:");
decimal c = 0.1M; // M suffix means a decimal literal value
decimal d = 0.2M;
if (c + d == 0.3M)
{
Console.WriteLine($"{c} + {d} equals 0.3");
}
else
{
Console.WriteLine($"{c} + {d} does NOT equal 0.3");
}
#!markdown
## Storing Booleans
#!csharp
bool happy = true;
bool sad = false;
#!markdown
## Storing any type of object
#!csharp
object height = 1.88; // storing a double in an object
object name = "Amir"; // storing a string in an object
Console.WriteLine($"{name} is {height} metres tall.");
//int length1 = name.Length; // gives compile error!
int length2 = ((string)name).Length; // tell compiler it is a string
Console.WriteLine($"{name} has {length2} characters.");
#!markdown
## Storing dynamic types
#!csharp
// storing a string in a dynamic object
// string has a Length property
dynamic anotherName = "Ahmed";
// int does not have a Length property
anotherName = 12;
// an array of any type has a Length property
anotherName = new[] { 3, 5, 7 };
// this compiles but would throw an exception at run-time
// if you later store a data type that does not have a
// property named Length
Console.WriteLine($"Length is {anotherName.Length}");
#!markdown
## Specifying and inferring the type of a local variable
*Note*: click **Execute Code** in the following cell to import the namespaces for the subsequent code cell.
#!csharp
using System.IO;
using System.Xml;
#!csharp
var population = 66_000_000; // 66 million in UK
var weight = 1.88; // in kilograms
var price = 4.99M; // in pounds sterling
var fruit = "Apples"; // strings use double-quotes
var letter = 'Z'; // chars use single-quotes
var happy = true; // Booleans have value of true or false
// good use of var because it avoids the repeated type
// as shown in the more verbose second statement
var xml1 = new XmlDocument();
XmlDocument xml2 = new XmlDocument();
// bad use of var because we cannot tell the type, so we
// should use a specific type declaration as shown in
// the second statement
var file1 = File.CreateText(@"C:\something.txt");
StreamWriter file2 = File.CreateText(@"C:\something.txt");
XmlDocument xml3 = new(); // target-typed new in C# 9 or later
#!csharp
class Person
{
public DateTime BirthDate;
}
Person kim = new();
kim.BirthDate = new(1967, 12, 26); // instead of: new DateTime(1967, 12, 26)
#!markdown
## Getting default values for types
The default value of a `string` is `null` which outputs as nothing.
#!csharp
Console.WriteLine($"default(int) = {default(int)}");
Console.WriteLine($"default(bool) = {default(bool)}");
Console.WriteLine(
$"default(DateTime) = {default(DateTime)}");
Console.WriteLine(
$"default(string) = {default(string)}");
#!csharp
int number = 13;
Console.WriteLine($"number has been set to: {number}");
number = default;
Console.WriteLine($"number has been reset to its default: {number}");
#!markdown
## Storing multiple values an array
#!csharp
string[] names; // can reference any array of strings
// allocating memory for four strings in an array
names = new string[4];
// storing items at index positions
names[0] = "Kate";
names[1] = "Jack";
names[2] = "Rebecca";
names[3] = "Tom";
// looping through the names
for (int i = 0; i < names.Length; i++)
{
// output the item at index position i
Console.WriteLine(names[i]);
}
#!markdown
## Making a value type nullable
#!csharp
int thisCannotBeNull = 4;
// thisCannotBeNull = null; // compile error!
int? thisCouldBeNull = null;
Console.WriteLine(thisCouldBeNull);
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
thisCouldBeNull = 7;
Console.WriteLine(thisCouldBeNull);
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
#!markdown
## Checking for null
#!csharp
string authorName = null;
// the following throws a NullReferenceException
// int x = authorName.Length;
// instead of throwing an exception, null is assigned to y
int? y = authorName?.Length;
Console.WriteLine($"y is null: {y is null}");
// result will be 3 if authorName?.Length is null
var result = authorName?.Length ?? 3;
Console.WriteLine(result);
#!markdown
## Formatting using numbered positional arguments
#!csharp
using static System.Console;
#!csharp
int numberOfApples = 12;
decimal pricePerApple = 0.35M;
WriteLine(
format: "{0} apples costs {1:C}",
arg0: numberOfApples,
arg1: pricePerApple * numberOfApples);
string formatted = string.Format(
format: "{0} apples costs {1:C}",
arg0: numberOfApples,
arg1: pricePerApple * numberOfApples);
//WriteToFile(formatted); // writes the string into a file
#!markdown
## Formatting using interpolated strings
#!csharp
WriteLine($"{numberOfApples} apples costs {pricePerApple * numberOfApples:C}");
#!markdown
## Understanding format strings
#!csharp
string applesText = "Apples";
int applesCount = 1234;
string bananasText = "Bananas";
int bananasCount = 56789;
WriteLine(
format: "{0,-8} {1,6:N0}",
arg0: "Name",
arg1: "Count");
WriteLine(
format: "{0,-8} {1,6:N0}",
arg0: applesText,
arg1: applesCount);
WriteLine(
format: "{0,-8} {1,6:N0}",
arg0: bananasText,
arg1: bananasCount);
#!markdown
## Getting text input from the user
.NET Interactive notebooks do not support `ReadLine()` so in the following code we must set literal string values for the two variables.
#!csharp
Write("Type your first name and press ENTER: ");
string firstName = "Gary"; // cannot use Console.ReadLine()
Write("Type your age and press ENTER: ");
string age = "34"; // cannot use Console.ReadLine()
WriteLine(
$"Hello {firstName}, you look good for {age}.");
#!markdown
## Getting key input from the user
`Console.ReadKey()` does not work in a .NET notebook.
#!markdown
## Getting arguments
Arguments cannot be passed to a .NET notebook.
#!markdown
## Exercise 3 - Practice number sizes and ranges
#!csharp
WriteLine("--------------------------------------------------------------------------");
WriteLine("Type Byte(s) of memory Min Max");
WriteLine("--------------------------------------------------------------------------");
WriteLine($"sbyte {sizeof(sbyte),-4} {sbyte.MinValue,30} {sbyte.MaxValue,30}");
WriteLine($"byte {sizeof(byte),-4} {byte.MinValue,30} {byte.MaxValue,30}");
WriteLine($"short {sizeof(short),-4} {short.MinValue,30} {short.MaxValue,30}");
WriteLine($"ushort {sizeof(ushort),-4} {ushort.MinValue,30} {ushort.MaxValue,30}");
WriteLine($"int {sizeof(int),-4} {int.MinValue,30} {int.MaxValue,30}");
WriteLine($"uint {sizeof(uint),-4} {uint.MinValue,30} {uint.MaxValue,30}");
WriteLine($"long {sizeof(long),-4} {long.MinValue,30} {long.MaxValue,30}");
WriteLine($"ulong {sizeof(ulong),-4} {ulong.MinValue,30} {ulong.MaxValue,30}");
WriteLine($"float {sizeof(float),-4} {float.MinValue,30} {float.MaxValue,30}");
WriteLine($"double {sizeof(double),-4} {double.MinValue,30} {double.MaxValue,30}");
WriteLine($"decimal {sizeof(decimal),-4} {decimal.MinValue,30} {decimal.MaxValue,30}");
WriteLine("--------------------------------------------------------------------------");

582
notebooks/Chapter03.dib Normal file
View file

@ -0,0 +1,582 @@
#!markdown
# Chapter 3 - Controlling Flow and Converting Types
#!markdown
The following code cell statically imports the Console class so that we can call the `WriteLine` method anywhere.
#!csharp
using static System.Console;
#!markdown
# Operating on variables
#!markdown
## Exploring unary operators
#!csharp
int a = 3;
int b = a++;
WriteLine($"a is {a}, b is {b}");
int c = 3;
int d = ++c; // increment c before assigning it
WriteLine($"c is {c}, d is {d}");
#!markdown
## Exploring binary arithmetic operators
#!csharp
int e = 11;
int f = 3;
WriteLine($"e is {e}, f is {f}");
WriteLine($"e + f = {e + f}");
WriteLine($"e - f = {e - f}");
WriteLine($"e * f = {e * f}");
WriteLine($"e / f = {e / f}");
WriteLine($"e % f = {e % f}");
double g = 11.0;
WriteLine($"g is {g:N1}, f is {f}");
WriteLine($"g / f = {g / f}");
#!markdown
## Exploring logical operators
#!csharp
bool a = true;
bool b = false;
WriteLine($"AND | a | b ");
WriteLine($"a | {a & a,-5} | {a & b,-5} ");
WriteLine($"b | {b & a,-5} | {b & b,-5} ");
WriteLine();
WriteLine($"OR | a | b ");
WriteLine($"a | {a | a,-5} | {a | b,-5} ");
WriteLine($"b | {b | a,-5} | {b | b,-5} ");
WriteLine();
WriteLine($"XOR | a | b ");
WriteLine($"a | {a ^ a,-5} | {a ^ b,-5} ");
WriteLine($"b | {b ^ a,-5} | {b ^ b,-5} ");
#!markdown
## Exploring conditional logical operators
#!csharp
private static bool DoStuff()
{
WriteLine("I am doing some stuff.");
return true;
}
#!csharp
WriteLine($"a & DoStuff() = {a & DoStuff()}");
WriteLine($"b & DoStuff() = {b & DoStuff()}");
#!csharp
WriteLine($"a && DoStuff() = {a && DoStuff()}");
WriteLine($"b && DoStuff() = {b && DoStuff()}");
#!markdown
## Exploring bitwise and binary shift operators
#!csharp
int a = 10; // 0000 1010
int b = 6; // 0000 0110
WriteLine($"a = {a}");
WriteLine($"b = {b}");
WriteLine($"a & b = {a & b}"); // 2-bit column only
WriteLine($"a | b = {a | b}"); // 8, 4, and 2-bit columns
WriteLine($"a ^ b = {a ^ b}"); // 8 and 4-bit columns
#!markdown
## Miscellaneous operators
#!csharp
int age = 47;
// How many operators in the following statement?
char firstDigit = age.ToString()[0];
// There are four operators:
// = is the assignment operator
// . is the member access operator
// () is the invocation operator
// [] is the indexer access operator
#!markdown
# Understanding selection statements
#!markdown
## Branching with the if statement
#!csharp
string password = "ninja";
if (password.Length < 8)
{
WriteLine("Your password is too short. Use at least 8 characters.");
}
else
{
WriteLine("Your password is strong.");
}
#!markdown
## Pattern matching with the if statement
#!csharp
// add and remove the "" to change the behavior
object o = 3;
int j = 4;
if (o is int i)
{
WriteLine($"{i} x {j} = {i * j}");
}
else
{
WriteLine("o is not an int so it cannot multiply!");
}
#!markdown
## Branching with the switch statement
#!csharp
int number = (new Random()).Next(1, 7);
WriteLine($"My random number is {number}");
switch (number)
{
case 1:
WriteLine("One");
break; // jumps to end of switch statement
case 2:
WriteLine("Two");
goto case 1;
case 3: // multiple case section
case 4:
WriteLine("Three or four");
goto case 1;
case 5:
// go to sleep for half a second
System.Threading.Thread.Sleep(500);
goto A_label;
default:
WriteLine("Default");
break;
} // end of switch statement
WriteLine("After end of switch");
A_label:
WriteLine($"After A_label");
#!markdown
## Pattern matching with the switch statement
#!csharp
using System.IO;
#!markdown
`ReadKey` is not supported in .NET Interactive Notebooks. To simulate the user pressing R or W, the following code instantiates a ConsoleKeyInfo object that represents the R or W key being pressed.
#!markdown
Comment and uncomment the appropriate path depending on your OS.
#!csharp
//string path = "/Users/markjprice/Code/Chapter03";
string path = @"C:\Code\Chapter03";
Write("Press R for read-only or W for writeable: ");
ConsoleKeyInfo key = // ReadKey();
new ConsoleKeyInfo('R', ConsoleKey.R,
shift: false, alt: false, control: false);
WriteLine();
Stream s = null;
if (key.Key == ConsoleKey.R)
{
s = File.Open(
Path.Combine(path, "file.txt"),
FileMode.OpenOrCreate,
FileAccess.Read);
}
else
{
s = File.Open(
Path.Combine(path, "file.txt"),
FileMode.OpenOrCreate,
FileAccess.Write);
}
string message = string.Empty;
switch (s)
{
case FileStream writeableFile when s.CanWrite:
message = "The stream is a file that I can write to.";
break;
case FileStream readOnlyFile:
message = "The stream is a read-only file.";
break;
case MemoryStream ms:
message = "The stream is a memory address.";
break;
default: // always evaluated last despite its current position
message = "The stream is some other type.";
break;
case null:
message = "The stream is null.";
break;
}
WriteLine(message);
#!markdown
## Simplifying switch statements with switch expressions
#!csharp
message = s switch
{
FileStream writeableFile when s.CanWrite
=> "The stream is a file that I can write to.",
FileStream readOnlyFile
=> "The stream is a read-only file.",
MemoryStream ms
=> "The stream is a memory address.",
null
=> "The stream is null.",
_
=> "The stream is some other type."
};
WriteLine(message);
#!markdown
# Understanding iteration statements
#!markdown
## Looping with the while statement
#!csharp
int x = 0;
while (x < 10)
{
WriteLine(x);
x++;
}
#!markdown
## Looping with the do statement
#!csharp
string password = string.Empty;
int attempts = 0;
do
{
if (attempts == 10)
{
break;
}
Write("Enter your password: ");
password = "Pa$$w0rd"; // ReadLine();
attempts++;
}
while (password != "Pa$$w0rd");
if (attempts == 10)
{
WriteLine("You failed after 10 attempts!");
}
else
{
WriteLine("Correct!");
}
#!markdown
## Looping with the for statement
#!csharp
for (int y = 1; y <= 10; y++)
{
WriteLine(y);
}
#!markdown
## Looping with the foreach statement
#!csharp
string[] names = { "Adam", "Barry", "Charlie" };
foreach (string name in names)
{
WriteLine($"{name} has {name.Length} characters.");
}
#!markdown
# Casting and converting between types
#!csharp
using static System.Console;
using static System.Convert;
#!markdown
## Casting numbers implicitly and explicitly
#!csharp
int a = 10;
double b = a; // an int can be safely cast into a double
WriteLine(b);
double c = 9.8;
int d = (int)c; // compiler gives an error for this line before adding (int)
WriteLine(d); // d is 9 losing the .8 part due to the (int) cast above
long e = 10;
int f = (int)e;
WriteLine($"e is {e:N0} and f is {f:N0}");
e = 5_000_000_000; // long.MaxValue;
f = (int)e;
WriteLine($"e is {e:N0} and f is {f:N0}");
#!markdown
## Converting with the System.Convert type
#!csharp
double g = 9.8;
int h = ToInt32(g); // a method of System.Convert
WriteLine($"g is {g} and h is {h}");
#!markdown
## Understanding the default rounding rules
#!csharp
double[] doubles = new[]
{ 9.49, 9.5, 9.51, 10.49, 10.5, 10.51 };
foreach (double n in doubles)
{
WriteLine($"ToInt32({n}) is {ToInt32(n)}");
}
#!markdown
## Taking control of rounding rules
#!csharp
foreach (double n in doubles)
{
WriteLine(format:
"Math.Round({0}, 0, MidpointRounding.AwayFromZero) is {1}",
arg0: n,
arg1: Math.Round(value: n, digits: 0,
mode: MidpointRounding.AwayFromZero));
}
#!markdown
## Converting from any type to a string
#!csharp
int number = 12;
WriteLine(number.ToString());
bool boolean = true;
WriteLine(boolean.ToString());
DateTime now = DateTime.Now;
WriteLine(now.ToString());
object me = new object();
WriteLine(me.ToString());
#!markdown
## Converting from a binary object to a string
#!csharp
// allocate array of 128 bytes
byte[] binaryObject = new byte[128];
// populate array with random bytes
(new Random()).NextBytes(binaryObject);
WriteLine("Binary Object as bytes:");
for(int index = 0; index < binaryObject.Length; index++)
{
Write($"{binaryObject[index]:X} ");
}
WriteLine();
// convert to Base64 string and output as text
string encoded = ToBase64String(binaryObject);
WriteLine($"Binary Object as Base64: {encoded}");
#!markdown
## Parsing from strings to numbers or dates and times
#!csharp
int age = int.Parse("27");
DateTime birthday = DateTime.Parse("4 July 1980");
WriteLine($"I was born {age} years ago.");
WriteLine($"My birthday is {birthday}.");
WriteLine($"My birthday is {birthday:D}.");
#!markdown
## Errors using Parse
#!csharp
int count = int.Parse("abc");
#!markdown
## Avoiding exceptions using the TryParse method
#!csharp
Write("How many eggs are there? ");
string input = "12"; // change to "twelve"
if (int.TryParse(input, out int count))
{
WriteLine($"There are {count} eggs.");
}
else
{
WriteLine("I could not parse the input.");
}
#!markdown
## Wrapping error-prone code in a try block
#!csharp
WriteLine("Before parsing");
Write("What is your age? ");
string input = "49";
try
{
int age = int.Parse(input);
WriteLine($"You are {age} years old.");
}
catch (OverflowException)
{
WriteLine("Your age is a valid number format but it is either too big or small.");
}
catch (FormatException)
{
WriteLine("The age you entered is not a valid number format.");
}
catch (Exception ex)
{
WriteLine($"{ex.GetType()} says {ex.Message}");
}
WriteLine("After parsing");
#!markdown
## Throwing overflow exceptions with the checked statement
#!csharp
try
{
checked
{
int x = int.MaxValue - 1;
WriteLine($"Initial value: {x}");
x++;
WriteLine($"After incrementing: {x}");
x++;
WriteLine($"After incrementing: {x}");
x++;
WriteLine($"After incrementing: {x}");
}
}
catch (OverflowException)
{
WriteLine("The code overflowed but I caught the exception.");
}
#!markdown
## Disabling compiler overflow checks with the unchecked statement
#!csharp
int y = int.MaxValue + 1;
#!csharp
unchecked
{
int y = int.MaxValue + 1;
WriteLine($"Initial value: {y}");
y--;
WriteLine($"After decrementing: {y}");
y--;
WriteLine($"After decrementing: {y}");
}

245
notebooks/Chapter04.dib Normal file
View file

@ -0,0 +1,245 @@
#!markdown
# Chapter 4 - Writing, Debugging, and Testing Functions
#!csharp
// allow simplified WriteLine and other console method calls
using static System.Console;
#!markdown
## Writing a times table function
#!csharp
static void TimesTable(byte number)
{
WriteLine($"This is the {number} times table:");
for (int row = 1; row <= 12; row++)
{
WriteLine($"{row} x {number} = {row * number}");
}
WriteLine();
}
#!csharp
TimesTable(6) // semicolon is optional in a .NET notebook
#!csharp
TimesTable(number: 87) // optional to specify the parameter name
#!markdown
## Writing a function that returns a value
#!csharp
static decimal CalculateTax(
decimal amount, string twoLetterRegionCode)
{
decimal rate = 0.0M;
switch (twoLetterRegionCode)
{
case "CH": // Switzerland
rate = 0.08M;
break;
case "DK": // Denmark
case "NO": // Norway
rate = 0.25M;
break;
case "GB": // United Kingdom
case "FR": // France
rate = 0.2M;
break;
case "HU": // Hungary
rate = 0.27M;
break;
case "OR": // Oregon
case "AK": // Alaska
case "MT": // Montana
rate = 0.0M;
break;
case "ND": // North Dakota
case "WI": // Wisconsin
case "ME": // Maine
case "VA": // Virginia
rate = 0.05M;
break;
case "CA": // California
rate = 0.0825M;
break;
default: // most US states
rate = 0.06M;
break;
}
return amount * rate;
}
#!csharp
decimal taxToPay = CalculateTax(149, "FR");
WriteLine($"You must pay {taxToPay} in tax.");
#!markdown
## Converting numbers from cardinal to ordinal
#!csharp
/// <summary>
/// Pass a 32-bit integer and it will be converted into its ordinal equivalent.
/// </summary>
/// <param name="number">Number is a cardinal value e.g. 1, 2, 3, and so on.</param>
/// <returns>Number as an ordinal value e.g. 1st, 2nd, 3rd, and so on.</ returns>
static string CardinalToOrdinal(int number)
{
switch (number)
{
case 11: // special cases for 11th to 13th
case 12:
case 13:
return $"{number}th";
default:
int lastDigit = number % 10;
string suffix = lastDigit switch
{
1 => "st",
2 => "nd",
3 => "rd",
_ => "th"
};
return $"{number}{suffix}";
}
}
#!csharp
static void RunCardinalToOrdinal()
{
for (int number = 1; number <= 40; number++)
{
Write($"{CardinalToOrdinal(number)} ");
}
WriteLine();
}
#!csharp
RunCardinalToOrdinal()
#!markdown
## Calculating factorials with recursion
#!csharp
static int Factorial(int number)
{
if (number < 1)
{
return 0;
}
else if (number == 1)
{
return 1;
}
else
{
checked // for overflow
{
return number * Factorial(number - 1);
}
}
}
#!csharp
static void RunFactorial()
{
for (int i = 1; i < 15; i++)
{
try
{
WriteLine($"{i}! = {Factorial(i):N0}");
}
catch (System.OverflowException)
{
WriteLine($"{i}! is too big for a 32-bit integer.");
}
}
}
#!csharp
RunFactorial()
#!markdown
## Using lambdas in function implementations
#!csharp
static int FibImperative(int term)
{
if (term == 1)
{
return 0;
}
else if (term == 2)
{
return 1;
}
else
{
return FibImperative(term - 1) + FibImperative(term - 2);
}
}
#!csharp
static void RunFibImperative()
{
for (int i = 1; i <= 30; i++)
{
WriteLine("The {0} term of the Fibonacci sequence is {1:N0}.",
arg0: CardinalToOrdinal(i),
arg1: FibImperative(term: i));
}
}
#!csharp
RunFibImperative()
#!csharp
static int FibFunctional(int term) =>
term switch
{
1 => 0,
2 => 1,
_ => FibFunctional(term - 1) + FibFunctional(term - 2)
};
#!csharp
static void RunFibFunctional()
{
for (int i = 1; i <= 30; i++)
{
WriteLine("The {0} term of the Fibonacci sequence is {1:N0}.",
arg0: CardinalToOrdinal(i),
arg1: FibFunctional(term: i));
}
}
#!csharp
RunFibFunctional()

655
notebooks/Chapter05.dib Normal file
View file

@ -0,0 +1,655 @@
#!markdown
# Chapter 5 - Building Your Own Types with Object-Oriented Programming
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
We cannot create a class library in a .NET Interactive notebook so we will put all code in the same notebook.
#!csharp
using static System.Console;
using System.Collections.Generic;
#!markdown
## Storing a value using an enum type (defining the enum)
#!csharp
[System.Flags]
public enum WondersOfTheAncientWorld : byte
{
None = 0b_0000_0000, // i.e. 0
GreatPyramidOfGiza = 0b_0000_0001, // i.e. 1
HangingGardensOfBabylon = 0b_0000_0010, // i.e. 2
StatueOfZeusAtOlympia = 0b_0000_0100, // i.e. 4
TempleOfArtemisAtEphesus = 0b_0000_1000, // i.e. 8
MausoleumAtHalicarnassus = 0b_0001_0000, // i.e. 16
ColossusOfRhodes = 0b_0010_0000, // i.e. 32
LighthouseOfAlexandria = 0b_0100_0000 // i.e. 64
}
#!markdown
## Defining a class
.NET Interactive notebooks do not support namespaces. You would get a "Cannot declare namesapce in script code" error. But we can declare a class.
#!markdown
## Storing a value using an enum type (using the enum)
#!csharp
public class Person : object
{
// fields
public string Name;
public DateTime DateOfBirth;
public WondersOfTheAncientWorld FavoriteAncientWonder;
public WondersOfTheAncientWorld BucketList;
public List<Person> Children = new();
// constants
public const string Species = "Homo Sapien";
// read-only fields
public readonly string HomePlanet = "Earth";
public readonly DateTime Instantiated;
// constructors
public Person()
{
// set default values for fields
// including read-only fields
Name = "Unknown";
Instantiated = DateTime.Now;
}
public Person(string initialName, string homePlanet)
{
Name = initialName;
HomePlanet = homePlanet;
Instantiated = DateTime.Now;
}
// methods
public void WriteToConsole()
{
WriteLine($"{Name} was born on a {DateOfBirth:dddd}.");
}
public string GetOrigin()
{
return $"{Name} was born on {HomePlanet}.";
}
public (string, int) GetFruit()
{
return ("Apples", 5);
}
public (string Name, int Number) GetNamedFruit()
{
return (Name: "Apples", Number: 5);
}
public string SayHello()
{
return $"{Name} says 'Hello!'";
}
public string SayHello(string name)
{
return $"{Name} says 'Hello {name}!'";
}
public string OptionalParameters(
string command = "Run!",
double number = 0.0,
bool active = true)
{
return string.Format(
format: "command is {0}, number is {1}, active is {2}",
arg0: command,
arg1: number,
arg2: active);
}
public void PassingParameters(int x, ref int y, out int z)
{
// out parameters cannot have a default
// AND must be initialized inside the method
z = 99;
// increment each parameter
x++;
y++;
z++;
}
// a property defined using C# 1 - 5 syntax
public string Origin
{
get
{
return $"{Name} was born on {HomePlanet}";
}
}
// two properties defined using C# 6+ lambda expression body syntax
public string Greeting => $"{Name} says 'Hello!'";
public int Age => System.DateTime.Today.Year - DateOfBirth.Year;
public string FavoriteIceCream { get; set; } // auto-syntax
private string favoritePrimaryColor;
public string FavoritePrimaryColor
{
get
{
return favoritePrimaryColor;
}
set
{
switch (value.ToLower())
{
case "red":
case "green":
case "blue":
favoritePrimaryColor = value;
break;
default:
throw new System.ArgumentException(
$"{value} is not a primary color. " +
"Choose from: red, green, blue.");
}
}
}
// indexers
public Person this[int index]
{
get
{
return Children[index]; // pass on to the List<T> indexer
}
set
{
Children[index] = value;
}
}
// end of class
}
#!markdown
## Instantiating a class
The namespace for a class defined in a .NET Interactive notebook follows the pattern `Submission#[number]+[classname]`.
#!csharp
// Person bob = new Person(); // C# 1.0 or later
// var bob = new Person(); // C# 3.0 or later
Person bob = new(); // C# 9.0 or later
WriteLine(bob.ToString());
bob.Name = "Bob Smith";
bob.DateOfBirth = new DateTime(1965, 12, 22); // C# 1.0 or later
WriteLine(format: "{0} was born on {1:dddd, d MMMM yyyy}",
arg0: bob.Name,
arg1: bob.DateOfBirth);
Person alice = new()
{
Name = "Alice Jones",
DateOfBirth = new(1998, 3, 7) // C# 9.0 or later
};
WriteLine(format: "{0} was born on {1:dd MMM yy}",
arg0: alice.Name,
arg1: alice.DateOfBirth);
#!csharp
bob.FavoriteAncientWonder = WondersOfTheAncientWorld.StatueOfZeusAtOlympia;
WriteLine(
format: "{0}'s favorite wonder is {1}. Its integer is {2}.",
arg0: bob.Name,
arg1: bob.FavoriteAncientWonder,
arg2: (int)bob.FavoriteAncientWonder);
bob.BucketList =
WondersOfTheAncientWorld.HangingGardensOfBabylon
| WondersOfTheAncientWorld.MausoleumAtHalicarnassus;
// bob.BucketList = (WondersOfTheAncientWorld)18;
WriteLine($"{bob.Name}'s bucket list is {bob.BucketList}");
#!markdown
## Storing multiple values using collections
#!csharp
bob.Children.Add(new Person { Name = "Alfred" });
bob.Children.Add(new Person { Name = "Zoe" });
WriteLine(
$"{bob.Name} has {bob.Children.Count} children:");
for (int childIndex = 0; childIndex < bob.Children.Count; childIndex++)
{
WriteLine($" {bob.Children[childIndex].Name}");
}
#!markdown
## Making a field static
#!csharp
public class BankAccount
{
public string AccountName; // instance member
public decimal Balance; // instance member
public static decimal InterestRate; // shared member
}
#!csharp
BankAccount.InterestRate = 0.012M; // store a shared value
var jonesAccount = new BankAccount();
jonesAccount.AccountName = "Mrs. Jones";
jonesAccount.Balance = 2400;
WriteLine(format: "{0} earned {1:C} interest.",
arg0: jonesAccount.AccountName,
arg1: jonesAccount.Balance * BankAccount.InterestRate);
var gerrierAccount = new BankAccount();
gerrierAccount.AccountName = "Ms. Gerrier";
gerrierAccount.Balance = 98;
WriteLine(format: "{0} earned {1:C} interest.",
arg0: gerrierAccount.AccountName,
arg1: gerrierAccount.Balance * BankAccount.InterestRate);
#!markdown
## Making a field constant
#!csharp
WriteLine($"{bob.Name} is a {Person.Species}");
#!markdown
## Making a field read-only
#!csharp
WriteLine($"{bob.Name} was born on {bob.HomePlanet}");
#!markdown
## Initializing fields with constructors
#!csharp
var blankPerson = new Person();
WriteLine(format:
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
arg0: blankPerson.Name,
arg1: blankPerson.HomePlanet,
arg2: blankPerson.Instantiated);
#!markdown
## Defining multiple constructors
#!csharp
var gunny = new Person("Gunny", "Mars");
WriteLine(format:
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
arg0: gunny.Name,
arg1: gunny.HomePlanet,
arg2: gunny.Instantiated);
#!markdown
## Returning values from methods
#!csharp
bob.WriteToConsole();
WriteLine(bob.GetOrigin());
#!markdown
## Combining multiple returned values using tuples
#!csharp
public class TextAndNumber
{
public string Text;
public int Number;
}
public class LifeTheUniverseAndEverything
{
public TextAndNumber GetTheData()
{
return new TextAndNumber
{
Text = "What's the meaning of life?",
Number = 42
};
}
}
#!markdown
### Language support for tuples
#!csharp
(string, int) fruit = bob.GetFruit();
WriteLine($"{fruit.Item1}, {fruit.Item2} there are.");
#!markdown
### Naming the fields of a tuple
#!csharp
var fruitNamed = bob.GetNamedFruit();
WriteLine($"There are {fruitNamed.Number} {fruitNamed.Name}.");
#!markdown
### Inferring tuple names
#!csharp
var thing1 = ("Neville", 4);
WriteLine($"{thing1.Item1} has {thing1.Item2} children.");
var thing2 = (bob.Name, bob.Children.Count);
WriteLine($"{thing2.Name} has {thing2.Count} children.");
#!markdown
### Deconstructing tuples
#!csharp
(string fruitName, int fruitNumber) = bob.GetFruit();
WriteLine($"Deconstructed: {fruitName}, {fruitNumber}");
#!markdown
## Defining and passing parameters to methods
#!csharp
WriteLine(bob.SayHello());
WriteLine(bob.SayHello("Emily"));
#!markdown
## Passing optional and named parameters
#!csharp
WriteLine(bob.OptionalParameters());
#!csharp
WriteLine(bob.OptionalParameters("Jump!", 98.5));
#!csharp
WriteLine(bob.OptionalParameters(
number: 52.7, command: "Hide!"));
#!csharp
WriteLine(bob.OptionalParameters("Poke!", active: false));
#!markdown
## Controlling how parameters are passed
When a parameter is passed into a method, it can be passed in one of three ways:
- By value (this is the default): Think of these as being in-only.
- By reference as a ref parameter: Think of these as being in-and-out.
- As an out parameter: Think of these as being out-only.
#!csharp
int a = 10;
int b = 20;
int c = 30;
WriteLine($"Before: a = {a}, b = {b}, c = {c}");
bob.PassingParameters(a, ref b, out c);
WriteLine($"After: a = {a}, b = {b}, c = {c}");
#!markdown
## Simplified out parameters
In C# 7.0 and later, we can simplify code that uses the out variables.
#!csharp
int d = 10;
int e = 20;
WriteLine($"Before: d = {d}, e = {e}, f doesn't exist yet!");
// simplified C# 7.0 or later syntax for the out parameter
bob.PassingParameters(d, ref e, out int f);
WriteLine($"After: d = {d}, e = {e}, f = {f}");
#!markdown
# Splitting classes using partial
Partial classes are not supported in .NET Interactive notebooks. I have added the code to the original class near the top of this notebook.
#!csharp
Person sam = new()
{
Name = "Sam",
DateOfBirth = new(1972, 1, 27)
};
WriteLine(sam.Origin);
WriteLine(sam.Greeting);
WriteLine(sam.Age);
#!csharp
sam.FavoriteIceCream = "Chocolate Fudge";
WriteLine($"Sam's favorite ice-cream flavor is {sam.FavoriteIceCream}.");
sam.FavoritePrimaryColor = "Red";
WriteLine($"Sam's favorite primary color is {sam.FavoritePrimaryColor}.");
#!markdown
# Defining indexers
#!csharp
sam.Children.Add(new() { Name = "Charlie" });
sam.Children.Add(new() { Name = "Ella" });
WriteLine($"Sam's first child is {sam.Children[0].Name}");
WriteLine($"Sam's second child is {sam.Children[1].Name}");
WriteLine($"Sam's first child is {sam[0].Name}");
WriteLine($"Sam's second child is {sam[1].Name}");
#!markdown
# Pattern matching with objects
#!csharp
public class BusinessClassPassenger
{
public override string ToString()
{
return $"Business Class";
}
}
public class FirstClassPassenger
{
public int AirMiles { get; set; }
public override string ToString()
{
return $"First Class with {AirMiles:N0} air miles";
}
}
public class CoachClassPassenger
{
public double CarryOnKG { get; set; }
public override string ToString()
{
return $"Coach Class with {CarryOnKG:N2} KG carry on";
}
}
#!csharp
object[] passengers = {
new FirstClassPassenger { AirMiles = 1_419 },
new FirstClassPassenger { AirMiles = 16_562 },
new BusinessClassPassenger(),
new CoachClassPassenger { CarryOnKG = 25.7 },
new CoachClassPassenger { CarryOnKG = 0 },
};
foreach (object passenger in passengers)
{
decimal flightCost = passenger switch
{
/*
FirstClassPassenger p when p.AirMiles > 35000 => 1500M,
FirstClassPassenger p when p.AirMiles > 15000 => 1750M,
FirstClassPassenger _ => 2000M, */
// C# 9 or later syntax
FirstClassPassenger p => p.AirMiles switch
{
> 35000 => 1500M,
> 15000 => 1750M,
_ => 2000M
},
BusinessClassPassenger => 1000M,
CoachClassPassenger p when p.CarryOnKG < 10.0 => 500M,
CoachClassPassenger => 650M,
_ => 800M
};
WriteLine($"Flight costs {flightCost:C} for {passenger}");
}
#!markdown
# Working with records
#!markdown
## Init-only properties
#!csharp
#nullable enable
public class ImmutablePerson
{
public string? FirstName { get; init; }
public string? LastName { get; init; }
}
#!csharp
ImmutablePerson jeff = new()
{
FirstName = "Jeff",
LastName = "Winger"
};
jeff.FirstName = "Geoff";
#!markdown
## Understanding records
#!csharp
#nullable enable
public record ImmutableVehicle
{
public int Wheels { get; init; }
public string? Color { get; init; }
public string? Brand { get; init; }
}
#!csharp
ImmutableVehicle car = new()
{
Brand = "Mazda MX-5 RF",
Color = "Soul Red Crystal Metallic",
Wheels = 4
};
ImmutableVehicle repaintedCar = car
with { Color = "Polymetal Grey Metallic" };
WriteLine($"Original car color was {car.Color}.");
WriteLine($"New car color is {repaintedCar.Color}.");
#!markdown
## Simplifying data members in records
#!csharp
// simpler way to define a record
// auto-generates the properties, constructor, and deconstructor
public record ImmutableAnimal(string Name, string Species);
#!csharp
ImmutableAnimal oscar = new("Oscar", "Labrador");
var (who, what) = oscar; // calls Deconstruct method
WriteLine($"{who} is a {what}.");

860
notebooks/Chapter06.dib Normal file
View file

@ -0,0 +1,860 @@
#!markdown
# Chapter 6 - Implementing Interfaces and Inheriting Classes
#!csharp
using static System.Console;
#!csharp
public class PersonException : Exception
{
public PersonException() : base() { }
public PersonException(string message) : base(message) { }
public PersonException(string message, Exception innerException)
: base(message, innerException) { }
}
#!csharp
#nullable enable // required in notebook cells that use nullability
public class Person : object, IComparable<Person>
{
// fields
public string? Name; // ? allows null
public DateTime DateOfBirth;
public List<Person> Children = new(); // C# 9 or later
// methods
public void WriteToConsole()
{
WriteLine($"{Name} was born on a {DateOfBirth:dddd}.");
}
// static method to "multiply"
public static Person Procreate(Person p1, Person p2)
{
Person baby = new()
{
Name = $"Baby of {p1.Name} and {p2.Name}"
};
p1.Children.Add(baby);
p2.Children.Add(baby);
return baby;
}
// instance method to "multiply"
public Person ProcreateWith(Person partner)
{
return Procreate(this, partner);
}
// operator to "multiply"
public static Person operator *(Person p1, Person p2)
{
return Person.Procreate(p1, p2);
}
// method with a local function
public static int Factorial(int number)
{
if (number < 0)
{
throw new ArgumentException(
$"{nameof(number)} cannot be less than zero.");
}
return localFactorial(number);
int localFactorial(int localNumber) // local function
{
if (localNumber < 1) return 1;
return localNumber * localFactorial(localNumber - 1);
}
}
// delegate field
public event EventHandler? Shout;
// data field
public int AngerLevel;
// method
public void Poke()
{
AngerLevel++;
if (AngerLevel >= 3)
{
// if something is listening...
if (Shout != null)
{
// ...then call the delegate
Shout(this, EventArgs.Empty);
}
}
}
public int CompareTo(Person? other)
{
if (Name is null) return 0;
return Name.CompareTo(other?.Name);
}
// overridden methods
public override string ToString()
{
return $"{Name} is a {base.ToString()}";
}
public void TimeTravel(DateTime when)
{
if (when <= DateOfBirth)
{
throw new PersonException("If you travel back in time to a date earlier than your own birth, then the universe will explode!");
}
else
{
WriteLine($"Welcome to {when:yyyy}!");
}
}
// end of class
}
#!markdown
# Implementing functionality using methods
#!csharp
Person harry = new() { Name = "Harry" };
Person mary = new() { Name = "Mary" };
Person jill = new() { Name = "Jill" };
// call instance method
Person baby1 = mary.ProcreateWith(harry);
baby1.Name = "Gary";
// call static method
Person baby2 = Person.Procreate(harry, jill);
// call an operator
Person baby3 = harry * mary;
WriteLine($"{harry.Name} has {harry.Children.Count} children.");
WriteLine($"{mary.Name} has {mary.Children.Count} children.");
WriteLine($"{jill.Name} has {jill.Children.Count} children.");
WriteLine(
format: "{0}'s first child is named \"{1}\".",
arg0: harry.Name,
arg1: harry.Children[0].Name);
#!markdown
## Implementing functionality using local functions
#!csharp
WriteLine($"5! is {Person.Factorial(5)}");
#!markdown
# Raising and handling events
#!markdown
## Defining and handling delegates
#!csharp
#nullable enable
static void Harry_Shout(object? sender, EventArgs e)
{
if (sender is null) return;
Person p = (Person)sender;
WriteLine($"{p.Name} is this angry: {p.AngerLevel}.");
}
#!csharp
harry.Shout += Harry_Shout;
#!csharp
harry.Poke();
harry.Poke();
harry.Poke();
harry.Poke();
#!markdown
# Making types safely reusable with generics
#!markdown
## Working with non-generic types
#!csharp
// non-generic lookup collection
System.Collections.Hashtable lookupObject = new();
lookupObject.Add(key: 1, value: "Alpha");
lookupObject.Add(key: 2, value: "Beta");
lookupObject.Add(key: 3, value: "Gamma");
lookupObject.Add(key: harry, value: "Delta");
int key = 2; // lookup the value that has 2 as its key
WriteLine(format: "Key {0} has value: {1}",
arg0: key,
arg1: lookupObject[key]);
// lookup the value that has harry as its key
WriteLine(format: "Key {0} has value: {1}",
arg0: harry,
arg1: lookupObject[harry]);
#!markdown
## Working with generic types
#!csharp
// generic lookup collection
Dictionary<int, string> lookupIntString = new();
lookupIntString.Add(key: 1, value: "Alpha");
lookupIntString.Add(key: 2, value: "Beta");
lookupIntString.Add(key: 3, value: "Gamma");
lookupIntString.Add(key: 4, value: "Delta");
key = 3;
WriteLine(format: "Key {0} has value: {1}",
arg0: key,
arg1: lookupIntString[key]);
#!markdown
# Implementing interfaces
#!markdown
## Comparing objects when sorting
#!csharp
Person[] people =
{
new() { Name = "Simon" },
new() { Name = "Jenny" },
new() { Name = "Adam" },
new() { Name = "Richard" }
};
WriteLine("Initial list of people:");
foreach (Person p in people)
{
WriteLine($" {p.Name}");
}
WriteLine("Use Person's IComparable implementation to sort:");
Array.Sort(people);
foreach (Person p in people)
{
WriteLine($" {p.Name}");
}
#!markdown
## Comparing objects using a separate class
#!csharp
#nullable enable
public class PersonComparer : IComparer<Person>
{
public int Compare(Person? x, Person? y)
{
if (x is null || y is null)
{
return 0;
}
// Compare the Name lengths...
int result = x.Name.Length.CompareTo(y.Name.Length);
// ...if they are equal...
if (result == 0)
{
// ...then compare by the Names...
return x.Name.CompareTo(y.Name);
}
else // result will be -1 or 1
{
// ...otherwise compare by the lengths.
return result;
}
}
}
#!csharp
WriteLine("Use PersonComparer's IComparer implementation to sort:");
Array.Sort(people, new PersonComparer());
foreach (Person p in people)
{
WriteLine($" {p.Name}");
}
#!markdown
## Implicit and explicit interface implementations
#!csharp
public interface IGamePlayer
{
void Lose();
}
public interface IKeyHolder
{
void Lose();
}
public class Human : IGamePlayer, IKeyHolder
{
public void Lose() // implicit implementation
{
// implement losing a key
WriteLine("Lost my key!");
}
void IGamePlayer.Lose() // explicit implementation
{
// implement losing a game
WriteLine("Lost the game!");
}
}
// calling implicit and explicit implementations of Lose
Human p = new();
p.Lose(); // calls implicit implementation of losing a key
((IGamePlayer)p).Lose(); // calls explicit implementation of losing a game
IGamePlayer player = p as IGamePlayer;
player.Lose(); // calls explicit implementation of losing a game
#!markdown
## Defining interfaces with default implementations
Note that the code cell below executes successfully despite the `DvdPlayer` class not implementing `Stop`.
#!csharp
public interface IPlayable
{
void Play();
void Pause();
void Stop() // default interface implementation
{
WriteLine("Default implementation of Stop.");
}
}
public class DvdPlayer : IPlayable
{
public void Pause()
{
WriteLine("DVD player is pausing.");
}
public void Play()
{
WriteLine("DVD player is playing.");
}
}
#!markdown
# Managing memory with reference and value types
#!markdown
## How reference and value types are stored in memory
#!csharp
int number1 = 49;
long number2 = 12;
System.Drawing.Point location = new(x: 4, y: 5);
Person kevin = new() { Name = "Kevin",
DateOfBirth = new(year: 1988, month: 9, day: 23) };
Person sally;
#!markdown
## Equality of types
#!csharp
int a = 3;
int b = 3;
WriteLine($"a == b: {(a == b)}"); // true
#!csharp
Person2 a = new() { Name = "Kevin" };
Person2 b = new() { Name = "Kevin" };
WriteLine($"a == b: {(a == b)}"); // false
#!csharp
Person2 a = new() { Name = "Kevin" };
Person2 b = a;
WriteLine($"a == b: {(a == b)}"); // true
#!csharp
string a = "Kevin";
string b = "Kevin";
WriteLine($"a == b: {(a == b)}"); // true
#!markdown
# Defining struct types
#!csharp
public struct DisplacementVector
{
public int X;
public int Y;
public DisplacementVector(int initialX, int initialY)
{
X = initialX;
Y = initialY;
}
public static DisplacementVector operator +(
DisplacementVector vector1,
DisplacementVector vector2)
{
return new(
vector1.X + vector2.X,
vector1.Y + vector2.Y);
}
}
#!csharp
DisplacementVector dv1 = new(3, 5);
DisplacementVector dv2 = new(-2, 7);
DisplacementVector dv3 = dv1 + dv2;
WriteLine($"({dv1.X}, {dv1.Y}) + ({dv2.X}, {dv2.Y}) = ({dv3.X}, {dv3.Y})");
#!markdown
## Working with record struct types
#!csharp
public record struct DisplacementVector(int X, int Y);
#!markdown
## Releasing unmanaged resources
#!csharp
public class Animal
{
public Animal() // constructor
{
// allocate any unmanaged resources
}
~Animal() // Finalizer aka destructor
{
// deallocate any unmanaged resources
}
}
#!csharp
public class Animal : IDisposable
{
public Animal()
{
// allocate unmanaged resource
}
~Animal() // Finalizer
{
Dispose(false);
}
bool disposed = false; // have resources been released?
public void Dispose()
{
Dispose(true);
// tell garbage collector it does not need to call the finalizer
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
// deallocate the *unmanaged* resource
// ...
if (disposing)
{
// deallocate any other *managed* resources
// ...
}
disposed = true;
}
}
#!csharp
using (Animal a = new())
{
// code that uses the Animal instance
}
#!csharp
Animal a = new();
try
{
// code that uses the Animal instance
}
finally
{
if (a != null) a.Dispose();
}
#!markdown
# Working with null values
#!csharp
int thisCannotBeNull = 4;
//thisCannotBeNull = null; // compile error!
int? thisCouldBeNull = null;
WriteLine(thisCouldBeNull);
WriteLine(thisCouldBeNull.GetValueOrDefault());
thisCouldBeNull = 7;
WriteLine(thisCouldBeNull);
WriteLine(thisCouldBeNull.GetValueOrDefault());
#!markdown
## Declaring non-nullable variables and parameters
#!csharp
#nullable enable
class Address
{
public string? Building;
public string Street = string.Empty;
public string City = string.Empty;
public string Region = string.Empty;
}
Address address = new();
address.Building = null;
address.Street = null;
address.City = "London";
address.Region = null;
#!markdown
## Checking for null
#!csharp
string authorName = null;
// the following throws a NullReferenceException
//int x = authorName.Length;
// instead of throwing an exception, null is assigned to y
int? y = authorName?.Length;
if (!y.HasValue) WriteLine("y is null");
// result will be 3 if authorName?.Length is null
int result = authorName?.Length ?? 3;
Console.WriteLine(result);
#!markdown
# Inheriting from classes
#!csharp
#nullable enable
public class Employee : Person
{
public string? EmployeeCode { get; set; }
public DateTime HireDate { get; set; }
public new void WriteToConsole()
{
WriteLine(format:
"{0} was born on {1:dd/MM/yy} and hired on {2:dd/MM/yy}",
arg0: Name,
arg1: DateOfBirth,
arg2: HireDate);
}
public override string ToString()
{
return $"{Name}'s code is {EmployeeCode}";
}
}
#!csharp
Employee john = new()
{
Name = "John Jones",
DateOfBirth = new(year: 1990, month: 7, day: 28)
};
john.WriteToConsole();
#!markdown
## Extending classes to add functionality
#!csharp
john.EmployeeCode = "JJ001";
john.HireDate = new(year: 2014, month: 11, day: 23);
WriteLine($"{john.Name} was hired on {john.HireDate:dd/MM/yy}");
#!markdown
## Overriding members
#!csharp
WriteLine(john.ToString());
#!markdown
## Inheriting from abstract classes
#!csharp
public interface INoImplementation // C# 1.0 and later
{
void Alpha(); // must be implemented by derived type
}
public interface ISomeImplementation // C# 8.0 and later
{
void Alpha(); // must be implemented by derived type
void Beta()
{
// default implementation; can be overridden
}
}
public abstract class PartiallyImplemented // C# 1.0 and later
{
public abstract void Gamma(); // must be implemented by derived type
public virtual void Delta() // can be overridden
{
// implementation
}
}
public class FullyImplemented : PartiallyImplemented, ISomeImplementation
{
public void Alpha()
{
// implementation
}
public override void Gamma()
{
// implementation
}
}
// you can only instantiate the fully implemented class
FullyImplemented a = new();
// all the other types give compile errors
PartiallyImplemented b = new(); // compile error!
ISomeImplementation c = new(); // compile error!
INoImplementation d = new(); // compile error!
#!markdown
## Preventing inheritance and overriding
You can prevent another developer from inheriting from your class by applying the `sealed`
keyword to its definition.
#!csharp
public sealed class ScroogeMcDuck
{
}
#!markdown
You can prevent someone from further overriding a `virtual` method in your class by applying
the `sealed` keyword to the method. You can only seal an overridden method.
#!csharp
public class Singer
{
// virtual allows this method to be overridden
public virtual void Sing()
{
WriteLine("Singing...");
}
}
public class LadyGaga : Singer
{
// sealed prevents overriding the method in subclasses
public sealed override void Sing()
{
WriteLine("Singing with style...");
}
}
#!markdown
## Understanding polymorphism
#!csharp
Employee aliceInEmployee = new()
{ Name = "Alice", EmployeeCode = "AA123" };
Person aliceInPerson = aliceInEmployee;
aliceInEmployee.WriteToConsole();
aliceInPerson.WriteToConsole();
WriteLine(aliceInEmployee.ToString());
WriteLine(aliceInPerson.ToString());
#!markdown
## Casting within inheritance hierarchies
#!csharp
Employee explicitAlice = aliceInPerson;
#!csharp
if (aliceInPerson is Employee)
{
WriteLine($"{nameof(aliceInPerson)} IS an Employee");
Employee explicitAlice = (Employee)aliceInPerson;
// safely do something with explicitAlice
}
#!csharp
#nullable enable
Employee? aliceAsEmployee = aliceInPerson as Employee; // could be null
if (aliceAsEmployee != null)
{
WriteLine($"{nameof(aliceInPerson)} AS an Employee");
// safely do something with aliceAsEmployee
}
#!markdown
# Inheriting and extending .NET types
#!csharp
try
{
john.TimeTravel(when: new(1999, 12, 31));
john.TimeTravel(when: new(1950, 12, 25));
}
catch (PersonException ex)
{
WriteLine(ex.Message);
}
#!markdown
## Using static methods to reuse functionality
#!csharp
using System.Text.RegularExpressions;
#!csharp
public class StringExtensions
{
public static bool IsValidEmail(string input)
{
// use simple regular expression to check
// that the input string is a valid email
return Regex.IsMatch(input,
@"[a-zA-Z0-9\.-_]+@[a-zA-Z0-9\.-_]+");
}
}
#!csharp
string email1 = "pamela@test.com";
string email2 = "ian&test.com";
WriteLine("{0} is a valid e-mail address: {1}",
arg0: email1,
arg1: StringExtensions.IsValidEmail(email1));
WriteLine("{0} is a valid e-mail address: {1}",
arg0: email2,
arg1: StringExtensions.IsValidEmail(email2));
#!markdown
Extension methods must be defined in a top-level static method so they cannot be defined in a notebook.
#!markdown
# Using an analyzer to write better code
You cannot run code analyzers on notebook code.

80
notebooks/Chapter07.dib Normal file
View file

@ -0,0 +1,80 @@
#!markdown
# Chapter 7 - Packaging and Distributing .NET Types
Most of this chapter is about creating apps and class libraries for deployment so .NET Interactive notebooks cannot be used for most code examples.
#!markdown
## Importing a namespace to use a type
#!csharp
using System.Xml.Linq; // XDocument
#!csharp
XDocument doc = new();
#!markdown
## Relating C# keywords to .NET types
One of the common questions I get from new C# programmers is, "What is the difference
between `string` with a lowercase s and `String` with an uppercase S?"
#!csharp
using System; // String
#!csharp
string s1 = "Hello";
String s2 = "World";
#!markdown
## Understanding native-sized integers
C# 9 introduced `nint` and `nuint` keyword alias for native-sized integers, meaning that the
storage size for the integer value is platform specific.
#!csharp
using static System.Console;
#!csharp
WriteLine($"int.MaxValue = {int.MaxValue:N0}");
WriteLine($"nint.MaxValue = {nint.MaxValue:N0}");
#!markdown
## Testing your class library package
#!csharp
#r "nuget:packt.csdotnet.sharedlibrary,6.0.0"
#!csharp
using Packt.Shared;
#!csharp
#nullable enable
Write("Enter a color value in hex: ");
string? hex = "00ffc8"; // or ReadLine();
WriteLine("Is {0} a valid color value? {1}",
arg0: hex, arg1: hex.IsValidHex());
Write("Enter a XML element: ");
string? xmlTag = "<h1 class=\"<\" />"; // or ReadLine()
WriteLine("Is {0} a valid XML element? {1}",
arg0: xmlTag, arg1: xmlTag.IsValidXmlTag());
Write("Enter a password: ");
string? password = "secretsauce"; // or ReadLine()
WriteLine("Is {0} a valid password? {1}",
arg0: password, arg1: password.IsValidPassword());

878
notebooks/Chapter08.dib Normal file
View file

@ -0,0 +1,878 @@
#!markdown
# Chapter 8 - Working with Common .NET Types
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
#!csharp
using static System.Console;
#!markdown
# Working with numbers
One of the most common types of data is numbers.
#!csharp
using System.Numerics;
#!markdown
## Working with big integers
#!csharp
WriteLine("Working with large integers:");
WriteLine("----------------------------------------");
ulong big = ulong.MaxValue;
WriteLine($"{big,40:N0}");
BigInteger bigger =
BigInteger.Parse("123456789012345678901234567890");
WriteLine($"{bigger,40:N0}");
#!markdown
## Working with complex numbers
#!csharp
WriteLine("Working with complex numbers:");
Complex c1 = new(real: 4, imaginary: 2);
Complex c2 = new(real: 3, imaginary: 7);
Complex c3 = c1 + c2;
// output using default ToString implementation
WriteLine($"{c1} added to {c2} is {c3}"); // output using custom format
WriteLine("{0} + {1}i added to {2} + {3}i is {4} + {5}i",
c1.Real, c1.Imaginary,
c2.Real, c2.Imaginary,
c3.Real, c3.Imaginary);
#!markdown
# Working with text
One of the other most common types of data for variables is text.
#!csharp
string city = "London";
#!markdown
## Getting the length of a string
#!csharp
WriteLine($"{city} is {city.Length} characters long.");
#!markdown
## Getting the characters of a string
#!csharp
WriteLine($"First char is {city[0]} and third is {city[2]}.");
#!markdown
## Splitting a string
#!csharp
string cities = "Paris,Tehran,Chennai,Sydney,New York,Medellín";
string[] citiesArray = cities.Split(',');
WriteLine($"There are {citiesArray.Length} items in the array.");
foreach (string item in citiesArray)
{
WriteLine(item);
}
#!markdown
## Getting part of a string
Sometimes, you need to get part of some text. The `IndexOf` method has nine overloads that return the index position of a specified `char` or `string` within a `string`. The `Substring` method has two overloads, as shown in the following list:
- `Substring(startIndex, length)`: returns a substring starting at startIndex and containing the next length characters.
- `Substring(startIndex)`: returns a substring starting at startIndex and containing all characters up to the end of the string.
#!csharp
string fullName = "Alan Jones";
int indexOfTheSpace = fullName.IndexOf(' ');
string firstName = fullName.Substring(
startIndex: 0, length: indexOfTheSpace);
string lastName = fullName.Substring(
startIndex: indexOfTheSpace + 1);
WriteLine($"Original: {fullName}");
WriteLine($"Swapped: {lastName}, {firstName}");
#!markdown
## Checking a string for content
Sometimes, you need to check whether a piece of text starts or ends with some characters or contains some characters. You can achieve this with methods named `StartsWith`, `EndsWith`, and `Contains`:
#!csharp
string company = "Microsoft";
bool startsWithM = company.StartsWith("M");
bool containsN = company.Contains("N");
WriteLine($"Text: {company}");
WriteLine($"Starts with M: {startsWithM}, contains an N: {containsN}");
#!markdown
## Joining, formatting, and other string members
#!csharp
string recombined = string.Join(" => ", citiesArray);
WriteLine(recombined);
#!csharp
string fruit = "Apples";
decimal price = 0.39M;
DateTime when = DateTime.Today;
WriteLine($"Interpolated: {fruit} cost {price:C} on {when:dddd}.");
WriteLine(string.Format("string.Format: {0} cost {1:C} on {2:dddd}.",
arg0: fruit, arg1: price, arg2: when));
#!markdown
# Working with dates and times
After numbers and text, the next most popular types of data to work with are dates and times.
#!markdown
## Specifying date and time values
#!csharp
WriteLine("Earliest date/time value is: {0}",
arg0: DateTime.MinValue);
WriteLine("UNIX epoch date/time value is: {0}",
arg0: DateTime.UnixEpoch);
WriteLine("Date/time value Now is: {0}",
arg0: DateTime.Now);
WriteLine("Date/time value Today is: {0}",
arg0: DateTime.Today);
#!csharp
DateTime christmas = new(year: 2021, month: 12, day: 25);
WriteLine("Christmas: {0}",
arg0: christmas); // default format
WriteLine("Christmas: {0:dddd, dd MMMM yyyy}",
arg0: christmas); // custom format
WriteLine("Christmas is in month {0} of the year.",
arg0: christmas.Month);
WriteLine("Christmas is day {0} of the year.",
arg0: christmas.DayOfYear);
WriteLine("Christmas {0} is on a {1}.",
arg0: christmas.Year,
arg1: christmas.DayOfWeek);
#!csharp
DateTime beforeXmas = christmas.Subtract(TimeSpan.FromDays(12));
DateTime afterXmas = christmas.AddDays(12);
WriteLine("12 days before Christmas is: {0}",
arg0: beforeXmas);
WriteLine("12 days after Christmas is: {0}",
arg0: afterXmas);
TimeSpan untilChristmas = christmas - DateTime.Now;
WriteLine("There are {0} days and {1} hours until Christmas.",
arg0: untilChristmas.Days,
arg1: untilChristmas.Hours);
WriteLine("There are {0:N0} hours until Christmas.",
arg0: untilChristmas.TotalHours);
#!csharp
DateTime kidsWakeUp = new(
year: 2021, month: 12, day: 25,
hour: 6, minute: 30, second: 0);
WriteLine("Kids wake up on Christmas: {0}",
arg0: kidsWakeUp);
WriteLine("The kids woke me up at {0}",
arg0: kidsWakeUp.ToShortTimeString());
#!markdown
## Globalization with dates and times
The current culture controls how dates and times are parsed:
#!csharp
using System.Globalization;
#!csharp
// use the following line to switch to British culture
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
WriteLine("Current culture is: {0}",
arg0: CultureInfo.CurrentCulture.Name);
string textDate = "4 July 2021";
DateTime independenceDay = DateTime.Parse(textDate);
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
arg0: textDate,
arg1: independenceDay);
textDate = "7/4/2021";
independenceDay = DateTime.Parse(textDate);
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
arg0: textDate,
arg1: independenceDay);
independenceDay = DateTime.Parse(textDate,
provider: CultureInfo.GetCultureInfo("en-US"));
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
arg0: textDate,
arg1: independenceDay);
#!csharp
for (int year = 2020; year < 2026; year++)
{
Write($"{year} is a leap year: {DateTime.IsLeapYear(year)}. ");
WriteLine("There are {0} days in February {1}.",
arg0: DateTime.DaysInMonth(year: year, month: 2), arg1: year);
}
WriteLine("Is Christmas daylight saving time? {0}",
arg0: christmas.IsDaylightSavingTime());
WriteLine("Is July 4th daylight saving time? {0}",
arg0: independenceDay.IsDaylightSavingTime());
#!markdown
## Working with only a date or a time
.NET 6 introduces some new types for working with only a date value or only a time value named DateOnly and TimeOnly.
#!csharp
DateOnly queensBirthday = new(year: 2022, month: 4, day: 21);
WriteLine($"The Queen's next birthday is on {queensBirthday}.");
TimeOnly partyStarts = new(hour: 20, minute: 30);
WriteLine($"The Queen's party starts at {partyStarts}.");
DateTime calendarEntry = queensBirthday.ToDateTime(partyStarts);
WriteLine($"Add to your calendar: {calendarEntry}.");
#!markdown
# Pattern matching with regular expressions
Regular expressions are useful for validating input from the user.
#!csharp
using System.Text.RegularExpressions;
#!markdown
## Checking for digits entered as text
#!csharp
#nullable enable // required to support string?
// Write("Enter your age: ");
string? input = "34"; // ReadLine();
Regex ageChecker = new(@"^\d+$");
if (ageChecker.IsMatch(input))
{
WriteLine("Thank you!");
}
else
{
WriteLine($"This is not a valid age: {input}");
}
#!markdown
## Splitting a complex comma-separated string
Earlier in this chapter, you learned how to split a simple comma-separated `string` variable. But what about the following example of film titles?
```
"Monsters, Inc.","I, Tonya","Lock, Stock and Two Smoking Barrels"
```
#!csharp
string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\"";
WriteLine($"Films to split: {films}");
string[] filmsDumb = films.Split(',');
WriteLine("Splitting with string.Split method:");
foreach (string film in filmsDumb)
{
WriteLine(film);
}
#!csharp
WriteLine();
Regex csv = new(
"(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)");
MatchCollection filmsSmart = csv.Matches(films);
WriteLine("Splitting with regular expression:");
foreach (Match film in filmsSmart)
{
WriteLine(film.Groups[2].Value);
}
#!markdown
# Storing multiple objects in collections
Another of the most common types of data is collections. If you need to store multiple values in a variable, then you can use a collection.
#!csharp
static void Output(string title, IEnumerable<string> collection)
{
WriteLine(title);
foreach (string item in collection)
{
WriteLine($" {item}");
}
}
#!markdown
## Working with lists
#!csharp
static void WorkingWithLists()
{
// Simple syntax for creating a list and adding three items
List<string> cities = new();
cities.Add("London");
cities.Add("Paris");
cities.Add("Milan");
/* Alternative syntax that is converted by the compiler into
the three Add method calls above
List<string> cities = new()
{ "London", "Paris", "Milan" };
*/
/* Alternative syntax that passes an
array of string values to AddRange method
List<string> cities = new();
cities.AddRange(new[] { "London", "Paris", "Milan" });
*/
Output("Initial list", cities);
WriteLine($"The first city is {cities[0]}.");
WriteLine($"The last city is {cities[cities.Count - 1]}.");
cities.Insert(0, "Sydney");
Output("After inserting Sydney at index 0", cities);
cities.RemoveAt(1);
cities.Remove("Milan");
Output("After removing two cities", cities);
}
#!csharp
WorkingWithLists();
#!markdown
## Working with dictionaries
#!csharp
static void WorkingWithDictionaries()
{
Dictionary<string, string> keywords = new();
// add using named parameters
keywords.Add(key: "int", value: "32-bit integer data type");
// add using positional parameters
keywords.Add("long", "64-bit integer data type");
keywords.Add("float", "Single precision floating point number");
/* Alternative syntax; compiler converts this to calls to Add method
Dictionary<string, string> keywords = new()
{
{ "int", "32-bit integer data type" },
{ "long", "64-bit integer data type" },
{ "float", "Single precision floating point number" },
}; */
/* Alternative syntax; compiler converts this to calls to Add method
Dictionary<string, string> keywords = new()
{
["int"] = "32-bit integer data type",
["long"] = "64-bit integer data type",
["float"] = "Single precision floating point number"
}; */
Output("Dictionary keys:", keywords.Keys);
Output("Dictionary values:", keywords.Values);
WriteLine("Keywords and their definitions");
foreach (KeyValuePair<string, string> item in keywords)
{
WriteLine($" {item.Key}: {item.Value}");
}
// lookup a value using a key
string key = "long";
WriteLine($"The definition of {key} is {keywords[key]}");
}
#!csharp
WorkingWithDictionaries();
#!markdown
## Working with queues
#!csharp
static void WorkingWithQueues()
{
Queue<string> coffee = new();
coffee.Enqueue("Damir"); // front of queue
coffee.Enqueue("Andrea");
coffee.Enqueue("Ronald");
coffee.Enqueue("Amin");
coffee.Enqueue("Irina"); // back of queue
Output("Initial queue from front to back", coffee);
// server handles next person in queue
string served = coffee.Dequeue();
WriteLine($"Served: {served}.");
// server handles next person in queue
served = coffee.Dequeue();
WriteLine($"Served: {served}.");
Output("Current queue from front to back", coffee);
WriteLine($"{coffee.Peek()} is next in line.");
Output("Current queue from front to back", coffee);
}
#!csharp
WorkingWithQueues();
#!csharp
static void OutputPQ<TElement, TPriority>(string title,
IEnumerable<(TElement Element, TPriority Priority)> collection)
{
WriteLine(title);
foreach ((TElement, TPriority) item in collection)
{
WriteLine($" {item.Item1}: {item.Item2}");
}
}
#!csharp
static void WorkingWithPriorityQueues()
{
PriorityQueue<string, int> vaccine = new();
// add some people
// 1 = high priority people in their 70s or poor health
// 2 = medium priority e.g. middle aged
// 3 = low priority e.g. teens and twenties
vaccine.Enqueue("Pamela", 1); // my mum (70s)
vaccine.Enqueue("Rebecca", 3); // my niece (teens)
vaccine.Enqueue("Juliet", 2); // my sister (40s)
vaccine.Enqueue("Ian", 1); // my dad (70s)
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
vaccine.Enqueue("Mark", 2); // me (40s)
WriteLine($"{vaccine.Peek()} will be next to be vaccinated.");
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
}
#!csharp
WorkingWithPriorityQueues();
#!markdown
## Using immutable collections
#!csharp
using System.Collections.Immutable;
#!csharp
static void WorkingWithLists2()
{
// Simple syntax for creating a list and adding three items
List<string> cities = new();
cities.Add("London");
cities.Add("Paris");
cities.Add("Milan");
/* Alternative syntax that is converted by the compiler into
the three Add method calls above
List<string> cities = new()
{ "London", "Paris", "Milan" };
*/
/* Alternative syntax that passes an
array of string values to AddRange method
List<string> cities = new();
cities.AddRange(new[] { "London", "Paris", "Milan" });
*/
Output("Initial list", cities);
WriteLine($"The first city is {cities[0]}.");
WriteLine($"The last city is {cities[cities.Count - 1]}.");
cities.Insert(0, "Sydney");
Output("After inserting Sydney at index 0", cities);
cities.RemoveAt(1);
cities.Remove("Milan");
Output("After removing two cities", cities);
ImmutableList<string> immutableCities = cities.ToImmutableList();
ImmutableList<string> newList = immutableCities.Add("Rio");
Output("Immutable list of cities:", immutableCities);
Output("New list of cities:", newList);
}
#!csharp
WorkingWithLists2();
#!markdown
# Working with spans, indexes, and ranges
C# 8.0 introduced two features for identifying an item's index within an array and a range of items using two indexes.
#!markdown
## Identifying ranges with the Range type
#!csharp
Range r1 = new(start: new Index(3), end: new Index(7));
Range r2 = new(start: 3, end: 7); // using implicit int conversion
Range r3 = 3..7; // using C# 8.0 or later syntax
Range r4 = Range.StartAt(3); // from index 3 to last index
Range r5 = 3..; // from index 3 to last index
Range r6 = Range.EndAt(3); // from index 0 to index 3
Range r7 = ..3; // from index 0 to index 3
#!markdown
## Using indexes, ranges, and spans
#!csharp
string name = "Samantha Jones";
// Using Substring
int lengthOfFirst = name.IndexOf(' ');
int lengthOfLast = name.Length - lengthOfFirst - 1;
string firstName = name.Substring(
startIndex: 0,
length: lengthOfFirst);
string lastName = name.Substring(
startIndex: name.Length - lengthOfLast,
length: lengthOfLast);
WriteLine($"First name: {firstName}, Last name: {lastName}");
// Using spans
ReadOnlySpan<char> nameAsSpan = name.AsSpan();
ReadOnlySpan<char> firstNameSpan = nameAsSpan[0..lengthOfFirst];
ReadOnlySpan<char> lastNameSpan = nameAsSpan[^lengthOfLast..^0];
WriteLine("First name: {0}, Last name: {1}",
arg0: firstNameSpan.ToString(),
arg1: lastNameSpan.ToString());
#!markdown
# Working with network resources
Sometimes you will need to work with network resources.
#!markdown
## Working with URIs, DNS, and IP addresses
#!csharp
using System.Net; // IPHostEntry, Dns, IPAddress
#!csharp
#nullable enable
// Write("Enter a valid web address: ");
string? url = "http://google.com"; // ReadLine();
if (string.IsNullOrWhiteSpace(url))
{
url = "https://stackoverflow.com/search?q=securestring";
}
Uri uri = new(url);
WriteLine($"URL: {url}");
WriteLine($"Scheme: {uri.Scheme}");
WriteLine($"Port: {uri.Port}");
WriteLine($"Host: {uri.Host}");
WriteLine($"Path: {uri.AbsolutePath}");
WriteLine($"Query: {uri.Query}");
#!csharp
IPHostEntry entry = Dns.GetHostEntry(uri.Host);
WriteLine($"{entry.HostName} has the following IP addresses:");
foreach (IPAddress address in entry.AddressList)
{
WriteLine($" {address} ({address.AddressFamily})");
}
#!markdown
## Pinging a server
#!csharp
using System.Net.NetworkInformation; // Ping, PingReply, IPStatus
#!csharp
try
{
Ping ping = new();
WriteLine("Pinging server. Please wait...");
PingReply reply = ping.Send(uri.Host);
WriteLine($"{uri.Host} was pinged and replied: {reply.Status}.");
if (reply.Status == IPStatus.Success)
{
WriteLine("Reply from {0} took {1:N0}ms",
arg0: reply.Address,
arg1: reply.RoundtripTime);
}
}
catch (Exception ex)
{
WriteLine($"{ex.GetType().ToString()} says {ex.Message}");
}
#!markdown
# Working with reflection and attributes
Reflection is a programming feature that allows code to understand and manipulate itself.
#!csharp
using System.Reflection; // Assembly
#!markdown
## Reading assembly metadata
Note: This example works best in a console application. In a .NET Interactive notebook, the assembly is `Microsoft.DotNet.Interactive.App`.
#!csharp
#nullable enable
WriteLine("Assembly metadata:");
Assembly? assembly = Assembly.GetEntryAssembly();
if (assembly is null)
{
WriteLine("Failed to get entry assembly.");
return;
}
WriteLine($" Full name: {assembly.FullName}");
WriteLine($" Location: {assembly.Location}");
IEnumerable<Attribute> attributes = assembly.GetCustomAttributes();
WriteLine($" Assembly-level attributes:");
foreach (Attribute a in attributes)
{
WriteLine($" {a.GetType()}");
}
#!csharp
#nullable enable
AssemblyInformationalVersionAttribute? version = assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
WriteLine($" Version: {version?.InformationalVersion}");
AssemblyCompanyAttribute? company = assembly
.GetCustomAttribute<AssemblyCompanyAttribute>();
WriteLine($" Company: {company?.Company}");
#!markdown
## Creating custom attributes
This section should only be tried in a console application.
#!markdown
# Working with images
ImageSharp is a third-party cross-platform 2D graphics library.
#!csharp
#r "nuget:SixLabors.ImageSharp,1.0.3"
#!csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using System.IO;
#!csharp
string imagesFolder = Path.Combine(
Environment.CurrentDirectory, "images");
IEnumerable<string> images =
Directory.EnumerateFiles(imagesFolder);
foreach (string imagePath in images)
{
string thumbnailPath = Path.Combine(
Environment.CurrentDirectory, "images",
Path.GetFileNameWithoutExtension(imagePath)
+ "-thumbnail" + Path.GetExtension(imagePath));
using (Image image = Image.Load(imagePath))
{
image.Mutate(x => x.Resize(image.Width / 10, image.Height / 10));
image.Mutate(x => x.Grayscale());
image.Save(thumbnailPath);
}
}
WriteLine("Image processing complete. View the images folder.");
#!markdown
# Internationalizing your code
#!csharp
using System.Globalization; // CultureInfo
#!csharp
#nullable enable
CultureInfo globalization = CultureInfo.CurrentCulture;
CultureInfo localization = CultureInfo.CurrentUICulture;
WriteLine("The current globalization culture is {0}: {1}",
globalization.Name, globalization.DisplayName);
WriteLine("The current localization culture is {0}: {1}",
localization.Name, localization.DisplayName);
WriteLine();
WriteLine("en-US: English (United States)");
WriteLine("da-DK: Danish (Denmark)");
WriteLine("fr-CA: French (Canada)");
// Write("Enter an ISO culture code: ");
string? newCulture = "da-DK"; // ReadLine();
if (!string.IsNullOrEmpty(newCulture))
{
CultureInfo ci = new(newCulture);
// change the current cultures
CultureInfo.CurrentCulture = ci;
CultureInfo.CurrentUICulture = ci;
}
WriteLine();
// Write("Enter your name: ");
string? name = "Mikkel"; // ReadLine();
// Write("Enter your date of birth: ");
string? dob = "12/3/1980"; // ReadLine();
// Write("Enter your salary: ");
string? salary = "340000"; // ReadLine();
DateTime date = DateTime.Parse(dob);
int minutes = (int)DateTime.Today.Subtract(date).TotalMinutes;
decimal earns = decimal.Parse(salary);
WriteLine(
"{0} was born on a {1:dddd}, is {2:N0} minutes old, and earns {3:C}",
name, date, minutes, earns);

675
notebooks/Chapter09.dib Normal file
View file

@ -0,0 +1,675 @@
#!markdown
# Chapter 9 - Working with Files, Streams, and Serialization
- Managing the filesystem
- Reading and writing with streams
- Encoding and decoding text
- Serializing object graphs
- Controlling JSON processing
#!markdown
# Managing the filesystem
Your applications will often need to perform input and output operations with files and
directories in different environments. The `System` and `System.IO` namespaces contain classes
for this purpose.
#!markdown
## Handling cross-platform environments and filesystems
#!csharp
using static System.Console;
using static System.IO.Directory;
using static System.IO.Path;
using static System.Environment;
#!csharp
using System.IO;
#!csharp
WriteLine("{0,-33} {1}", arg0: "Path.PathSeparator",
arg1: PathSeparator);
WriteLine("{0,-33} {1}", arg0: "Path.DirectorySeparatorChar",
arg1: DirectorySeparatorChar);
WriteLine("{0,-33} {1}", arg0: "Directory.GetCurrentDirectory()",
arg1: GetCurrentDirectory());
WriteLine("{0,-33} {1}", arg0: "Environment.CurrentDirectory",
arg1: CurrentDirectory);
WriteLine("{0,-33} {1}", arg0: "Environment.SystemDirectory",
arg1: SystemDirectory);
WriteLine("{0,-33} {1}", arg0: "Path.GetTempPath()",
arg1: GetTempPath());
WriteLine("GetFolderPath(SpecialFolder");
WriteLine("{0,-33} {1}", arg0: " .System)",
arg1: GetFolderPath(SpecialFolder.System));
WriteLine("{0,-33} {1}", arg0: " .ApplicationData)",
arg1: GetFolderPath(SpecialFolder.ApplicationData));
WriteLine("{0,-33} {1}", arg0: " .MyDocuments)",
arg1: GetFolderPath(SpecialFolder.MyDocuments));
WriteLine("{0,-33} {1}", arg0: " .Personal)",
arg1: GetFolderPath(SpecialFolder.Personal));
#!markdown
## Managing drives
#!csharp
WriteLine("{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}",
"NAME", "TYPE", "FORMAT", "SIZE (BYTES)", "FREE SPACE");
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (drive.IsReady)
{
WriteLine(
"{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}",
drive.Name, drive.DriveType, drive.DriveFormat,
drive.TotalSize, drive.AvailableFreeSpace);
}
else
{
WriteLine("{0,-30} | {1,-10}", drive.Name, drive.DriveType);
}
}
#!markdown
## Managing directories
#!csharp
// define a directory path for a new folder
// starting in the user's folder
string newFolder = Combine(
GetFolderPath(SpecialFolder.Personal),
"Code", "Chapter09", "NewFolder");
WriteLine($"Working with: {newFolder}");
// check if it exists
WriteLine($"Does it exist? {Exists(newFolder)}");
// create directory
WriteLine("Creating it...");
CreateDirectory(newFolder);
WriteLine($"Does it exist? {Exists(newFolder)}");
Write("Confirm the directory exists, and then execute the next notebook cell.");
#!csharp
// delete directory
WriteLine("Deleting it...");
Delete(newFolder, recursive: true);
WriteLine($"Does it exist? {Exists(newFolder)}");
#!markdown
## Managing files
#!csharp
// define a directory path to output files
// starting in the user's folder
string dir = Combine(
GetFolderPath(SpecialFolder.Personal),
"Code", "Chapter09", "OutputFiles");
CreateDirectory(dir);
// define file paths
string textFile = Combine(dir, "Dummy.txt");
string backupFile = Combine(dir, "Dummy.bak");
WriteLine($"Working with: {textFile}");
// check if a file exists
WriteLine($"Does it exist? {File.Exists(textFile)}");
// create a new text file and write a line to it
StreamWriter textWriter = File.CreateText(textFile);
textWriter.WriteLine("Hello, C#!");
textWriter.Close(); // close file and release resources
WriteLine($"Does it exist? {File.Exists(textFile)}");
// copy the file, and overwrite if it already exists
File.Copy(sourceFileName: textFile,
destFileName: backupFile, overwrite: true);
WriteLine(
$"Does {backupFile} exist? {File.Exists(backupFile)}");
Write("Confirm the files exist, and then run the next notebook cell.");
#!csharp
// delete file
File.Delete(textFile);
WriteLine($"Does it exist? {File.Exists(textFile)}");
// read from the text file backup
WriteLine($"Reading contents of {backupFile}:");
StreamReader textReader = File.OpenText(backupFile);
WriteLine(textReader.ReadToEnd());
textReader.Close();
#!markdown
## Managing paths
#!csharp
// Managing paths
WriteLine($"Folder Name: {GetDirectoryName(textFile)}");
WriteLine($"File Name: {GetFileName(textFile)}");
WriteLine("File Name without Extension: {0}",
GetFileNameWithoutExtension(textFile));
WriteLine($"File Extension: {GetExtension(textFile)}");
WriteLine($"Random File Name: {GetRandomFileName()}");
WriteLine($"Temporary File Name: {GetTempFileName()}");
#!markdown
## Getting file information
#!csharp
FileInfo info = new(backupFile);
WriteLine($"{backupFile}:");
WriteLine($"Contains {info.Length} bytes");
WriteLine($"Last accessed {info.LastAccessTime}");
WriteLine($"Has readonly set to {info.IsReadOnly}");
#!markdown
## Controlling how you work with files
#!csharp
FileInfo info = new(backupFile);
WriteLine("Is the backup file compressed? {0}",
info.Attributes.HasFlag(FileAttributes.Compressed));
#!markdown
# Reading and writing with streams
#!markdown
## Writing to text streams
#!csharp
static class Viper
{
// define an array of Viper pilot call signs
public static string[] Callsigns = new[]
{
"Husker", "Starbuck", "Apollo", "Boomer",
"Bulldog", "Athena", "Helo", "Racetrack"
};
}
#!csharp
// define a file to write to
string textFile = Combine(CurrentDirectory, "streams.txt");
// create a text file and return a helper writer
StreamWriter text = File.CreateText(textFile);
// enumerate the strings, writing each one
// to the stream on a separate line
foreach (string item in Viper.Callsigns)
{
text.WriteLine(item);
}
text.Close(); // release resources
// output the contents of the file
WriteLine("{0} contains {1:N0} bytes.",
arg0: textFile,
arg1: new FileInfo(textFile).Length);
WriteLine(File.ReadAllText(textFile));
#!markdown
## Writing to XML streams
#!csharp
using System.Xml;
#!csharp
#nullable enable
FileStream? xmlFileStream = null;
XmlWriter? xml = null;
try
{
// define a file to write to
string xmlFile = Combine(CurrentDirectory, "streams.xml");
// create a file stream
xmlFileStream = File.Create(xmlFile);
// wrap the file stream in an XML writer helper
// and automatically indent nested elements
xml = XmlWriter.Create(xmlFileStream,
new XmlWriterSettings { Indent = true });
// write the XML declaration
xml.WriteStartDocument();
// write a root element
xml.WriteStartElement("callsigns");
// enumerate the strings writing each one to the stream
foreach (string item in Viper.Callsigns)
{
xml.WriteElementString("callsign", item);
}
// write the close root element
xml.WriteEndElement();
// close helper and stream
xml.Close();
xmlFileStream.Close();
// output all the contents of the file
WriteLine("{0} contains {1:N0} bytes.",
arg0: xmlFile,
arg1: new FileInfo(xmlFile).Length);
WriteLine(File.ReadAllText(xmlFile));
}
catch (Exception ex)
{
// if the path doesn't exist the exception will be caught
WriteLine($"{ex.GetType()} says {ex.Message}");
}
finally
{
if (xml != null)
{
xml.Dispose();
WriteLine("The XML writer's unmanaged resources have been disposed.");
if (xmlFileStream != null)
{
xmlFileStream.Dispose();
WriteLine("The file stream's unmanaged resources have been disposed.");
}
}
}
#!markdown
## Simplifying disposal by using the using statement
#!csharp
using FileStream file2 = File.OpenWrite(
Path.Combine(CurrentDirectory, "file2.txt"));
using StreamWriter writer2 = new(file2);
try
{
writer2.WriteLine("Welcome, .NET!");
}
catch(Exception ex)
{
WriteLine($"{ex.GetType()} says {ex.Message}");
}
#!markdown
## Compressing streams
#!csharp
using System.IO.Compression; // BrotliStream, GZipStream, CompressionMode
#!csharp
static void WorkWithCompression(bool useBrotli = true)
{
string fileExt = useBrotli ? "brotli" : "gzip";
// compress the XML output
string filePath = Combine(
CurrentDirectory, $"streams.{fileExt}");
FileStream file = File.Create(filePath);
Stream compressor;
if (useBrotli)
{
compressor = new BrotliStream(file, CompressionMode.Compress);
}
else
{
compressor = new GZipStream(file, CompressionMode.Compress);
}
using (compressor)
{
using (XmlWriter xml = XmlWriter.Create(compressor))
{
xml.WriteStartDocument();
xml.WriteStartElement("callsigns");
foreach (string item in Viper.Callsigns)
{
xml.WriteElementString("callsign", item);
}
}
} // also closes the underlying stream
// output all the contents of the compressed file
WriteLine("{0} contains {1:N0} bytes.",
filePath, new FileInfo(filePath).Length);
WriteLine($"The compressed contents:");
WriteLine(File.ReadAllText(filePath));
// read a compressed file
WriteLine("Reading the compressed XML file:");
file = File.Open(filePath, FileMode.Open);
Stream decompressor;
if (useBrotli)
{
decompressor = new BrotliStream(
file, CompressionMode.Decompress);
}
else
{
decompressor = new GZipStream(
file, CompressionMode.Decompress);
}
using (decompressor)
{
using (XmlReader reader = XmlReader.Create(decompressor))
{
while (reader.Read())
{
// check if we are on an element node named callsign
if ((reader.NodeType == XmlNodeType.Element)
&& (reader.Name == "callsign"))
{
reader.Read(); // move to the text inside element
WriteLine($"{reader.Value}"); // read its value
}
}
}
}
}
#!csharp
WorkWithCompression();
WorkWithCompression(useBrotli: false);
#!markdown
# Encoding and decoding text
Change the `ConsoleKey` value to D1 to D5 to select the encoding that you want to use.
#!csharp
// choose an encoding
Write("Press a number to choose an encoding: ");
ConsoleKey number = ConsoleKey.D3; // ReadKey(intercept: false).Key;
WriteLine();
WriteLine();
Encoding encoder = number switch
{
ConsoleKey.D1 => Encoding.ASCII,
ConsoleKey.D2 => Encoding.UTF7,
ConsoleKey.D3 => Encoding.UTF8,
ConsoleKey.D4 => Encoding.Unicode,
ConsoleKey.D5 => Encoding.UTF32,
_ => Encoding.Default
};
// define a string to encode
string message = "Café cost: £4.39";
// encode the string into a byte array
byte[] encoded = encoder.GetBytes(message);
// check how many bytes the encoding needed
WriteLine("{0} uses {1:N0} bytes.",
encoder.GetType().Name, encoded.Length);
WriteLine();
// enumerate each byte
WriteLine($"BYTE HEX CHAR");
foreach (byte b in encoded)
{
WriteLine($"{b,4} {b.ToString("X"),4} {(char)b,5}");
}
// decode the byte array back into a string and display it
string decoded = encoder.GetString(encoded);
WriteLine(decoded);
#!markdown
# Serializing object graphs
Serialization is the process of converting a live object into a sequence of bytes using a specified
format. Deserialization is the reverse process.
#!csharp
#nullable enable
public class Person
{
public Person() { } // required to use XmlSerializer
public Person(decimal initialSalary)
{
Salary = initialSalary;
}
public string? FirstName { get; set; }
public string? LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public HashSet<Person>? Children { get; set; }
protected decimal Salary { get; set; }
}
#!csharp
using System.Xml.Serialization; // XmlSerializer
#!csharp
// create an object graph
List<Person> people = new()
{
new(30000M)
{
FirstName = "Alice",
LastName = "Smith",
DateOfBirth = new(1974, 3, 14)
},
new(40000M)
{
FirstName = "Bob",
LastName = "Jones",
DateOfBirth = new(1969, 11, 23)
},
new(20000M)
{
FirstName = "Charlie",
LastName = "Cox",
DateOfBirth = new(1984, 5, 4),
Children = new()
{
new(0M)
{
FirstName = "Sally",
LastName = "Cox",
DateOfBirth = new(2000, 7, 12)
}
}
}
};
#!csharp
// create object that will format a List of Persons as XML
XmlSerializer xs = new(people.GetType());
WriteLine(people.GetType());
// create a file to write to
string path = Combine(CurrentDirectory, "people.xml");
WriteLine(path);
using (FileStream stream = File.Create(path))
{
// serialize the object graph to the stream
xs.Serialize(stream, people);
}
WriteLine("Written {0:N0} bytes of XML to {1}",
arg0: new FileInfo(path).Length,
arg1: path);
WriteLine();
// Display the serialized object graph
WriteLine(File.ReadAllText(path));
#!markdown
## Serializing with JSON
#!csharp
#r "nuget:Newtonsoft.Json,13.0.1"
#!csharp
// create a file to write to
string jsonPath = Combine(CurrentDirectory, "people.json");
using (StreamWriter jsonStream = File.CreateText(jsonPath))
{
// create an object that will format as JSON
Newtonsoft.Json.JsonSerializer jss = new();
// serialize the object graph into a string
jss.Serialize(jsonStream, people);
}
WriteLine();
WriteLine("Written {0:N0} bytes of JSON to: {1}",
arg0: new FileInfo(jsonPath).Length,
arg1: jsonPath);
// Display the serialized object graph
WriteLine(File.ReadAllText(jsonPath));
#!csharp
using NewJson = System.Text.Json.JsonSerializer;
#!csharp
using (FileStream jsonLoad = File.Open(jsonPath, FileMode.Open))
{
// deserialize object graph into a List of Person
List<Person>? loadedPeople =
await NewJson.DeserializeAsync(utf8Json: jsonLoad,
returnType: typeof(List<Person>)) as List<Person>;
if (loadedPeople is not null)
{
foreach (Person p in loadedPeople)
{
WriteLine("{0} has {1} children.",
p.LastName, p.Children?.Count ?? 0);
}
}
}
#!markdown
## Controlling JSON processing
#!csharp
using System.Text.Json; // JsonSerializer
using System.Text.Json.Serialization; // [JsonInclude]
#!csharp
#nullable enable
public class Book
{
// constructor to set non-nullable property
public Book(string title)
{
Title = title;
}
// properties
public string Title { get; set; }
public string? Author { get; set; }
// fields
[JsonInclude] // include this field
public DateOnly PublishDate;
[JsonInclude] // include this field
public DateTimeOffset Created;
public ushort Pages;
}
#!csharp
Book csharp10 = new(title:
"C# 10 and .NET 6 - Modern Cross-platform Development")
{
Author = "Mark J Price",
PublishDate = new(year: 2021, month: 11, day: 9),
Pages = 823,
Created = DateTimeOffset.UtcNow,
};
JsonSerializerOptions options = new()
{
IncludeFields = true, // includes all fields
PropertyNameCaseInsensitive = true,
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
string filePath = Combine(CurrentDirectory, "book.json");
using (Stream fileStream = File.Create(filePath))
{
JsonSerializer.Serialize<Book>(
utf8Json: fileStream, value: csharp10, options);
}
WriteLine("Written {0:N0} bytes of JSON to {1}",
arg0: new FileInfo(filePath).Length,
arg1: filePath);
WriteLine();
// Display the serialized object graph
WriteLine(File.ReadAllText(filePath));

159
notebooks/Chapter10.dib Normal file
View file

@ -0,0 +1,159 @@
#!markdown
# Chapter 10 - Working with Data Using Entity Framework Core
- Understanding modern databases
- Setting up EF Core
- Defining EF Core models
- Querying EF Core models
- Loading patterns with EF Core
- Manipulating data with EF Core
- Working with transactions
- Code First EF Core models
#!markdown
# Setting up EF Core
[.NET Interactive with SQL](https://devblogs.microsoft.com/dotnet/net-interactive-with-sql-net-notebooks-in-visual-studio-code/)
#!csharp
#r "nuget:Microsoft.DotNet.Interactive.SqlServer,1.0.0-beta.21553.5"
#!sql
#!connect mssql -h
#!sql
#!connect mssql --kernel-name Northwind "Data Source=.;Initial Catalog=Northwind;Integrated Security=true;MultipleActiveResultSets=true;"
#!sql
#!sql-Northwind
SELECT * FROM Categories
#!sql
#!connect mssql --create-dbcontext --kernel-name Northwind "Data Source=.;Initial Catalog=Northwind;Integrated Security=true;MultipleActiveResultSets=true;"
#!markdown
## Choosing an EF Core database provider
Add package references to the EF Core data provider for both SQL Server and SQLite.
#!csharp
#r "nuget:Microsoft.EntityFrameworkCore.Sqlite,5.0.11"
#r "nuget:Microsoft.EntityFrameworkCore.SqlServer,5.0.11"
#!markdown
Define a constant to switch between providers.
#!csharp
public class ProjectConstants
{
public const string DatabaseProvider = "SQLite"; // or "SQLServer"
}
#!csharp
using static System.Console;
#!csharp
WriteLine($"Using {ProjectConstants.DatabaseProvider} database provider.");
#!markdown
## Defining the Northwind database context class
#!csharp
using System.IO;
using Microsoft.EntityFrameworkCore; // DbContext, DbContextOptionsBuilder
#!csharp
public class Northwind : DbContext
{
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
if (ProjectConstants.DatabaseProvider == "SQLite")
{
string path = Path.Combine(
Environment.CurrentDirectory, "Northwind.db");
WriteLine($"Using {path} database file.");
optionsBuilder.UseSqlite($"Filename={path}");
}
else
{
string connection = "Data Source=.;" +
"Initial Catalog=Northwind;" +
"Integrated Security=true;" +
"MultipleActiveResultSets=true;";
optionsBuilder.UseSqlServer(connection);
}
}
}
#!markdown
## Defining the Category and Product entity classes
#!csharp
#nullable enable
using System.ComponentModel.DataAnnotations; // [Required], [StringLength]
using System.ComponentModel.DataAnnotations.Schema; // [Column]
public class Category
{
// these properties map to columns in the database
public int CategoryId { get; set; }
public string? CategoryName { get; set; }
[Column(TypeName = "ntext")]
public string? Description { get; set; }
// defines a navigation property for related rows
public virtual ICollection<Product> Products { get; set; }
public Category()
{
// to enable developers to add products to a Category we must
// initialize the navigation property to an empty collection
this.Products = new HashSet<Product>();
}
}
public class Product
{
public int ProductId { get; set; } // primary key
[Required]
[StringLength(40)]
public string ProductName { get; set; } = null!;
[Column("UnitPrice", TypeName = "money")]
public decimal? Cost { get; set; } // property name != column name
[Column("UnitsInStock")]
public short? Stock { get; set; }
public bool Discontinued { get; set; }
// these two define the foreign key relationship
// to the Categories table
public int CategoryId { get; set; }
public virtual Category Category { get; set; } = null!;
}

206
notebooks/Chapter11.dib Normal file
View file

@ -0,0 +1,206 @@
#!markdown
# Chapter 11 - Querying and Manipulating Data Using LINQ
- Writing LINQ expressions
- Working with sets using LINQ
- Using LINQ with EF Core
- Sweetening LINQ syntax with syntactic sugar
- Using multiple threads with parallel LINQ
- Creating your own LINQ extension methods
- Working with LINQ to XML
#!markdown
# Writing LINQ expressions
#!markdown
## Understanding deferred execution
#!csharp
using static System.Console;
#!csharp
// a string array is a sequence that implements IEnumerable<string>
string[] names = new[] { "Michael", "Pam", "Jim", "Dwight",
"Angela", "Kevin", "Toby", "Creed" };
WriteLine("Deferred execution");
// Question: Which names end with an M?
// (written using a LINQ extension method)
var query1 = names.Where(name => name.EndsWith("m"));
// Question: Which names end with an M?
// (written using LINQ query comprehension syntax)
var query2 = from name in names where name.EndsWith("m") select name;
#!csharp
// Answer returned as an array of strings containing Pam and Jim
string[] result1 = query1.ToArray();
// Answer returned as a list of strings containing Pam and Jim
List<string> result2 = query2.ToList();
// Answer returned as we enumerate over the results
foreach (string name in query1)
{
WriteLine(name); // outputs Pam
//names[2] = "Jimmy"; // change Jim to Jimmy
// on the second iteration Jimmy does not end with an M
}
#!markdown
## Filtering entities with Where
#!csharp
static bool NameLongerThanFour(string name)
{
return name.Length > 4;
}
#!csharp
WriteLine("Writing queries");
// var query = names.Where(
// new Func<string, bool>(NameLongerThanFour));
// var query = names.Where(NameLongerThanFour);
IOrderedEnumerable<string> query = names
.Where(name => name.Length > 4)
.OrderBy(name => name.Length)
.ThenBy(name => name);
foreach (string item in query)
{
WriteLine(item);
}
#!markdown
## Filtering by type
#!csharp
WriteLine("Filtering by type");
List<Exception> exceptions = new()
{
new ArgumentException(),
new SystemException(),
new IndexOutOfRangeException(),
new InvalidOperationException(),
new NullReferenceException(),
new InvalidCastException(),
new OverflowException(),
new DivideByZeroException(),
new ApplicationException()
};
#!csharp
IEnumerable<ArithmeticException> arithmeticExceptionsQuery =
exceptions.OfType<ArithmeticException>();
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
{
WriteLine(exception);
}
#!markdown
## Working with sets and bags using LINQ
#!csharp
static void Output(IEnumerable<string> cohort, string description = "")
{
if (!string.IsNullOrEmpty(description))
{
WriteLine(description);
}
Write(" ");
WriteLine(string.Join(", ", cohort.ToArray()));
WriteLine();
}
#!csharp
string[] cohort1 = new[]
{ "Rachel", "Gareth", "Jonathan", "George" };
string[] cohort2 = new[]
{ "Jack", "Stephen", "Daniel", "Jack", "Jared" };
string[] cohort3 = new[]
{ "Declan", "Jack", "Jack", "Jasmine", "Conor" };
Output(cohort1, "Cohort 1");
Output(cohort2, "Cohort 2");
Output(cohort3, "Cohort 3");
Output(cohort2.Distinct(), "cohort2.Distinct()");
/* DistinctBy is only available in .NET 6 and later
Output(cohort2.DistinctBy(name => name.Substring(0, 2)),
"cohort2.DistinctBy(name => name.Substring(0, 2)):");
*/
Output(cohort2.Union(cohort3), "cohort2.Union(cohort3)");
Output(cohort2.Concat(cohort3), "cohort2.Concat(cohort3)");
Output(cohort2.Intersect(cohort3), "cohort2.Intersect(cohort3)");
Output(cohort2.Except(cohort3), "cohort2.Except(cohort3)");
Output(cohort1.Zip(cohort2,(c1, c2) => $"{c1} matched with {c2}"),
"cohort1.Zip(cohort2)");
#!markdown
# Using LINQ with EF Core
.NET Interactive Notebooks for SQL Server does not yet support custom EF Core models.
#!markdown
# Using multiple threads with parallel LINQ
To see it in action, we will start with some code that only uses a single thread to calculate Fibonacci numbers for 45 integers.
#!csharp
using System.Diagnostics;
Stopwatch watch = new();
//Write("Press ENTER to start. ");
//ReadLine();
watch.Start();
int max = 45;
IEnumerable<int> numbers = Enumerable.Range(start: 1, count: max);
WriteLine($"Calculating Fibonacci sequence up to {max}. Please wait...");
int[] fibonacciNumbers = numbers.AsParallel()
.Select(number => Fibonacci(number))
.OrderBy(number => number)
.ToArray();
watch.Stop();
WriteLine("{0:#,##0} elapsed milliseconds.",
arg0: watch.ElapsedMilliseconds);
Write("Results:");
foreach (int number in fibonacciNumbers)
{
Write($" {number}");
}
static int Fibonacci(int term) =>
term switch
{
1 => 0,
2 => 1,
_ => Fibonacci(term - 1) + Fibonacci(term - 2)
};

BIN
notebooks/Northwind.db Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

File diff suppressed because it is too large Load diff