├── .vscode ├── settings.json └── tasks.json ├── CH06 ├── XSS │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── User.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Privacy.cshtml │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Privacy.cshtml.cs │ │ ├── Login.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Index.cshtml │ │ ├── Error.cshtml.cs │ │ ├── Error.cshtml │ │ └── User.cshtml.cs │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── csrf.html │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── FoobleXSS.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ └── Startup.cs └── LoginExample │ ├── UserContext.cs │ ├── UserEntity.cs │ ├── LoginExample.csproj │ ├── UserEF.cs │ └── User.cs ├── .eslintrc ├── CH03 ├── Shoppidy │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Home │ │ │ ├── Privacy.cshtml │ │ │ └── Index.cshtml │ │ └── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── Shoppidy.csproj │ ├── Models │ │ ├── IShipmentService.cs │ │ ├── ErrorViewModel.cs │ │ └── ShipmentAddress.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Properties │ │ └── launchSettings.json │ ├── Program.cs │ ├── Controllers │ │ └── HomeController.cs │ └── Startup.cs ├── Twistat │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Login.cshtml │ │ ├── Callback.cshtml │ │ ├── Privacy.cshtml │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Privacy.cshtml.cs │ │ ├── Error.cshtml.cs │ │ ├── Callback.cshtml.cs │ │ ├── Login.cshtml.cs │ │ └── Error.cshtml │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── Properties │ │ ├── serviceDependencies.json │ │ ├── serviceDependencies.local.json │ │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Program.cs │ ├── Twistat.csproj │ ├── Twitter.cs │ └── Startup.cs ├── HttpHandler │ ├── Handler.cs │ ├── Properties │ │ └── launchSettings.json │ ├── TempReader.csproj │ ├── HttpStatusCodeClass.cs │ └── Program.cs ├── TechnicalDebt │ ├── Class1.cs │ └── TechnicalDebt.csproj ├── IfElse │ ├── IfElse.csproj │ └── IDatabase.cs ├── OrderAPI │ ├── OrderAPI.csproj │ ├── PostalAddress.cs │ └── Shipping.cs ├── ClassesVsStructs │ ├── ClassesVsStructs.csproj │ └── Identifier.cs └── EmojiChat │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── EmojiChat.csproj │ ├── WeatherForecast.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Controllers │ ├── WeatherForecastController.cs │ └── StatsController.cs │ └── Startup.cs ├── CH05 ├── BlabberCore │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Shared │ │ │ ├── EditorTemplates │ │ │ │ └── BlabForm.cshtml │ │ │ ├── DisplayTemplates │ │ │ │ └── Blab.cshtml │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ └── Home │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Index.cshtml │ ├── blabber.db │ ├── wwwroot │ │ ├── favicon.ico │ │ └── Content │ │ │ └── Site.css │ ├── .eslintignore │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Program.cs │ ├── DB │ │ ├── BlabEntity.cs │ │ ├── BlabDb.cs │ │ └── BlabberContext.cs │ ├── Controllers │ │ ├── BlabController.cs │ │ └── HomeController.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Migrations │ │ ├── 20201117054127_Initial.cs │ │ ├── BlabberContextModelSnapshot.cs │ │ └── 20201117054127_Initial.Designer.cs │ ├── BlabberCore.csproj │ └── Startup.cs ├── Blabber │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── Shared │ │ │ ├── EditorTemplates │ │ │ │ └── BlabForm.cshtml │ │ │ ├── DisplayTemplates │ │ │ │ └── Blab.cshtml │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Home │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Index.cshtml │ │ └── Web.config │ ├── favicon.ico │ ├── App_Data │ │ └── blabber.db │ ├── Global.asax │ ├── DB │ │ ├── BlabberContext.cs │ │ ├── BlabEntity.cs │ │ └── BlabDb.cs │ ├── App_Start │ │ ├── RouteConfig.cs │ │ └── BundleConfig.cs │ ├── DefaultDependencyResolver.cs │ ├── Controllers │ │ ├── BlabController.cs │ │ └── HomeController.cs │ ├── Content │ │ └── Site.css │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Global.asax.cs │ └── packages.config ├── DI │ ├── DI.csproj │ └── Program.cs └── Blabber.Models │ ├── HomepageModel.cs │ ├── BlabForm.cs │ ├── Blabber.Models.csproj │ ├── DateTimeExtensions.cs │ ├── Blab.cs │ └── BlabStorage.cs ├── CH02 ├── Nullability │ ├── MoveResult.cs │ ├── Nullability.csproj │ ├── DbId.cs │ ├── Person.cs │ └── TopicService.cs ├── ValidationContext │ ├── MoveResult.cs │ ├── ValidationContext.csproj │ ├── Arrow.cs │ ├── Username.cs │ ├── ParameterTypes.cs │ ├── PostId.cs │ └── DbId.cs ├── Strings │ ├── Strings.csproj │ ├── Concat.cs │ ├── Locales.cs │ └── StringExample.cs ├── Algorithms │ ├── Algorithms.csproj │ ├── Dictionary.cs │ ├── Structs.cs │ └── ContainsAlgorithms.cs ├── Arrays │ ├── Arrays.csproj │ ├── Program.cs │ └── Arrays.cs ├── ReferenceVsValueTypes │ ├── ReferenceVsValueTypes.csproj │ └── Program.cs ├── HashCode │ ├── HashcodeCombine.cs │ ├── HashCodeTest.cs │ ├── ProperGetHashCode.cs │ ├── HashCode.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ └── HashCode.csproj.old └── Supercalifragilisticexpialidocious │ ├── Supercalifragilisticexpialidocious.csproj │ └── Program.cs ├── CH04 ├── Summer │ ├── SumHelper.cs │ └── Summer.csproj ├── DateUtils │ ├── DateUtils.csproj │ └── DateTimeExtensions.cs ├── Posts │ ├── Posts.csproj │ ├── IPostRepository.cs │ └── PostService.cs ├── User │ ├── User.csproj │ └── Username.cs └── DateUtilsTests │ ├── Tests.csproj │ ├── Program.cs │ ├── UsernameTest.cs │ └── FrameworkTests.cs ├── CH07 ├── Patterns │ ├── IDb.cs │ ├── Boolean.cs │ ├── Patterns.csproj │ ├── Badge.cs │ ├── BufferProcessorBenchmark.cs │ ├── StringController.cs │ ├── Program.cs │ ├── User.cs │ ├── ChecksumBenchmark.cs │ ├── UserPreferences.cs │ ├── BranchPrediction.cs │ ├── Checksum.cs │ ├── MemberAccessBenchmark.cs │ ├── BufferProcessor.cs │ └── Luhn.cs ├── IO │ ├── IO.csproj │ ├── Program.cs │ └── FileCopyBenchmark.cs └── BenchmarkRunner │ ├── SimpleBenchmarkRunner.csproj │ ├── SampleBenchmarkSuite.cs │ └── Program.cs ├── CH09 ├── InfiniteLoop │ ├── InfiniteLoop.csproj │ └── Program.cs └── Exceptions │ ├── Exceptions.csproj │ ├── IDatabase.cs │ ├── Int.cs │ ├── ImageAlbum.cs │ ├── Voting.cs │ ├── OrderController.cs │ └── Program.cs ├── CH08 └── Connections │ ├── Circle.cs │ ├── Properties │ └── launchSettings.json │ ├── Connections.csproj │ ├── UniqueIdGenerator.cs │ ├── AsyncCounter.cs │ ├── Program.cs │ ├── ApiTokensLockFree.cs │ ├── ParallelWeb.cs │ ├── CpuBound.cs │ ├── LimitedList.cs │ ├── ApiTokens.cs │ ├── Cache.cs │ └── Post.cs ├── streetcoder-nowin.slnf └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.preferCSharpExtension": true 3 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": "**/*" 4 | } 5 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /CH03/HttpHandler/Handler.cs: -------------------------------------------------------------------------------- 1 | namespace HttpHandler; 2 | 3 | public class Handler 4 | { 5 | } -------------------------------------------------------------------------------- /CH05/Blabber/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /CH05/Blabber/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH05/Blabber/favicon.ico -------------------------------------------------------------------------------- /CH06/XSS/Pages/User.cshtml: -------------------------------------------------------------------------------- 1 | @page "{username}" 2 | @model FoobleXSS.Pages.UserModel 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /CH05/BlabberCore/blabber.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH05/BlabberCore/blabber.db -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH06/XSS/wwwroot/favicon.ico -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH03/Shoppidy/wwwroot/favicon.ico -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH03/Twistat/wwwroot/favicon.ico -------------------------------------------------------------------------------- /CH05/Blabber/App_Data/blabber.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH05/Blabber/App_Data/blabber.db -------------------------------------------------------------------------------- /CH05/Blabber/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Blabber.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /CH05/BlabberCore/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssg/streetcoder/HEAD/CH05/BlabberCore/wwwroot/favicon.ico -------------------------------------------------------------------------------- /CH06/XSS/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @namespace FoobleXSS.Pages 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /CH02/Nullability/MoveResult.cs: -------------------------------------------------------------------------------- 1 | public enum MoveResult 2 | { 3 | Success, 4 | Unauthorized, 5 | AlreadyMoved 6 | } -------------------------------------------------------------------------------- /CH02/ValidationContext/MoveResult.cs: -------------------------------------------------------------------------------- 1 | public enum MoveResult 2 | { 3 | Success, 4 | Unauthorized, 5 | AlreadyMoved 6 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/.eslintignore: -------------------------------------------------------------------------------- 1 | # first line is for UTF-8 BOM safety 2 | # https://github.com/eslint/eslint/issues/11777 3 | 4 | Scripts/ -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Shoppidy 2 | @using Shoppidy.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Twistat 2 | @namespace Twistat.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /CH05/Blabber/Views/Shared/EditorTemplates/BlabForm.cshtml: -------------------------------------------------------------------------------- 1 | @model BlabForm 2 | @Html.TextAreaFor(m => m.Content, 3, 5, new { @class = "blabeditor" }) -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Blabber 2 | @using Blabber.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Login.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Twistat.Pages.LoginModel 3 | @{ 4 | ViewData["Title"] = "Login"; 5 | } 6 | 7 |

Login

8 | 9 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Shared/EditorTemplates/BlabForm.cshtml: -------------------------------------------------------------------------------- 1 | @model BlabForm 2 | @Html.TextAreaFor(m => m.Content, 3, 5, new { @class = "blabeditor" }) 3 | 4 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Callback.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Twistat.Pages.CallbackModel 3 | @{ 4 | ViewData["Title"] = "Callback"; 5 | } 6 | 7 |

Callback

8 | 9 | -------------------------------------------------------------------------------- /CH03/Twistat/Properties/serviceDependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "secrets1": { 4 | "type": "secrets" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /CH03/Twistat/Properties/serviceDependencies.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "secrets1": { 4 | "type": "secrets.user" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /CH04/Summer/SumHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Summer; 2 | 3 | public static class SumHelper 4 | { 5 | public static int Sum(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | } -------------------------------------------------------------------------------- /CH04/Summer/Summer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CH02/Strings/Strings.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CH03/TechnicalDebt/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TechnicalDebt; 4 | 5 | public interface ICache 6 | { 7 | T Get(string key, Func retrievalCode, TimeSpan expiresIn); 8 | } -------------------------------------------------------------------------------- /CH04/DateUtils/DateUtils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /CH03/TechnicalDebt/TechnicalDebt.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CH05/Blabber/Views/Shared/DisplayTemplates/Blab.cshtml: -------------------------------------------------------------------------------- 1 | @model Blab 2 |
3 | @Model.Content 4 |
5 | @Model.CreatedOn.ToString() 6 |
7 |
-------------------------------------------------------------------------------- /CH03/Shoppidy/Shoppidy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | disable 5 | 6 | 7 | -------------------------------------------------------------------------------- /CH04/Posts/Posts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH04/User/User.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH03/IfElse/IfElse.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH03/OrderAPI/OrderAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH05/DI/DI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Exe 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH02/Algorithms/Algorithms.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH02/Arrays/Arrays.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Exe 5 | 6 | 7 | -------------------------------------------------------------------------------- /CH03/HttpHandler/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "TempReader": { 4 | "commandName": "Project", 5 | "commandLineArgs": "38.66,-121.4004" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Models/IShipmentService.cs: -------------------------------------------------------------------------------- 1 | internal interface IShipmentService 2 | { 3 | ShippingFormValidationResult ValidateShippingForm(ShipmentAddress form); 4 | bool SaveShippingInfo(ShipmentAddress form); 5 | } 6 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /CH02/Arrays/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arrays; 4 | 5 | internal static class Program 6 | { 7 | public static void Main() 8 | { 9 | Console.WriteLine(Arrays.ArrayVsList()); 10 | } 11 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /CH07/Patterns/IDb.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Patterns; 4 | internal interface IDb 5 | { 6 | public IEnumerable GetBadges(); 7 | public IEnumerable GetVisibleBadgeNames(); 8 | } 9 | -------------------------------------------------------------------------------- /CH09/InfiniteLoop/InfiniteLoop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH02/ValidationContext/ValidationContext.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Shared/DisplayTemplates/Blab.cshtml: -------------------------------------------------------------------------------- 1 | @model Blab 2 |
3 | @Model.Content 4 |
5 | @((DateTime.Now - Model.CreatedOn).ToIntervalString()) 6 |
7 |
-------------------------------------------------------------------------------- /CH03/ClassesVsStructs/ClassesVsStructs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 1701;1702;IDE0130 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH06/XSS/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CH02/Strings/Concat.cs: -------------------------------------------------------------------------------- 1 | namespace Strings; 2 | class Concat 3 | { 4 | public string ConcatName(string firstName, string middleName, string lastName) 5 | { 6 | return firstName + " " + middleName + " " + lastName; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CH03/Shoppidy/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CH04/Posts/IPostRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Posts; 4 | 5 | public interface IPostRepository 6 | { 7 | IQueryable GetTrendingTagTable(); 8 | 9 | IQueryable GetYesterdaysTrendingTagTable(); 10 | } -------------------------------------------------------------------------------- /CH05/Blabber/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

Blabber is an anonymous social media that keeps 8 | content only for the last 24 hours.

-------------------------------------------------------------------------------- /CH02/ReferenceVsValueTypes/ReferenceVsValueTypes.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CH02/ValidationContext/Arrow.cs: -------------------------------------------------------------------------------- 1 | namespace ValidationContext; 2 | 3 | internal class Arrow 4 | { 5 | public int Sum1(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | 10 | public int Sum2(int a, int b) => a + b; 11 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CH03/IfElse/IDatabase.cs: -------------------------------------------------------------------------------- 1 | internal interface IDatabase 2 | { 3 | string GetCurrentCityByName(string normalizedFirstName, string normalizedLastName); 4 | 5 | bool UpdateCurrentCity(int id, string city); 6 | 7 | bool IsPersonActive(int id); 8 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

Blabber is an anonymous social media that keeps 8 | content only for the last 24 hours.

9 | -------------------------------------------------------------------------------- /CH06/XSS/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /CH03/EmojiChat/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /CH03/Twistat/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. -------------------------------------------------------------------------------- /CH05/BlabberCore/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. 5 | -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /CH05/Blabber.Models/HomepageModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Blabber 4 | { 5 | public class HomepageModel 6 | { 7 | public BlabForm Form { get; set; } 8 | 9 | public IEnumerable Blabs { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /CH03/Twistat/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |
6 |

Welcome

7 |

Learn about building Web apps with ASP.NET Core.

8 |
9 | -------------------------------------------------------------------------------- /CH06/LoginExample/UserContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace LoginExample; 4 | 5 | public class UserContext : DbContext 6 | { 7 | public DbSet Users { get; set; } = null!; 8 | 9 | public UserContext() : base() 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Shoppidy.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CH02/HashCode/HashcodeCombine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | internal class HashcodeCombine 4 | { 5 | public int TopicId { get; set; } 6 | public int EntryId { get; set; } 7 | 8 | public override int GetHashCode() 9 | { 10 | return HashCode.Combine(TopicId, EntryId); 11 | } 12 | } -------------------------------------------------------------------------------- /CH06/XSS/FoobleXSS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | fooble 7 | 1701;1702;IDE0130 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CH08/Connections/Circle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Connections; 8 | class Circle 9 | { 10 | public static double Area(double radius) => Math.PI * Math.Pow(radius, 2); 11 | } 12 | -------------------------------------------------------------------------------- /CH08/Connections/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Connections": { 4 | "commandName": "Project", 5 | "commandLineArgs": "https://www.google.com https://www.bing.com https://eksisozluk.com https://www.reddit.com https://twitter.com https://streetcoder.org" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /CH05/Blabber/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

8 | Blabber is an example from the book Street Coder 9 | from Manning Publications. 10 |

-------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

8 | Blabber is an example from the book Street Coder 9 | from Manning Publications. 10 |

11 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Home page"; 5 | } 6 | 7 |
8 |

Welcome

9 |

Learn about building Web apps with ASP.NET Core.

10 |
11 | -------------------------------------------------------------------------------- /CH02/Algorithms/Dictionary.cs: -------------------------------------------------------------------------------- 1 | namespace Algorithms; 2 | 3 | public class Birthdate 4 | { 5 | public int Year { get; set; } 6 | public int Month { get; set; } 7 | public int Day { get; set; } 8 | 9 | public override int GetHashCode() 10 | { 11 | return 4; // chosen by a fair dice roll 12 | } 13 | } -------------------------------------------------------------------------------- /CH02/Nullability/Nullability.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | disable 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CH03/EmojiChat/EmojiChat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | disable 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CH05/Blabber.Models/BlabForm.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Blabber 4 | { 5 | public class BlabForm 6 | { 7 | public const int MaxContentLength = 300; 8 | 9 | [Required] 10 | [MaxLength(MaxContentLength)] 11 | public string Content { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EmojiChat; 4 | 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } -------------------------------------------------------------------------------- /CH05/Blabber/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 6 | 7 | 8 |
9 |

Error.

10 |

An error occurred while processing your request.

11 |
12 | 13 | -------------------------------------------------------------------------------- /CH07/IO/IO.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Exe 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 6 | 7 | 8 |
9 |

Error.

10 |

An error occurred while processing your request.

11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /CH07/BenchmarkRunner/SimpleBenchmarkRunner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /CH03/OrderAPI/PostalAddress.cs: -------------------------------------------------------------------------------- 1 | public class PostalAddress 2 | { 3 | public string FirstName { get; set; } 4 | public string LastName { get; set; } 5 | public string Address1 { get; set; } 6 | public string Address2 { get; set; } 7 | public string City { get; set; } 8 | public string ZipCode { get; set; } 9 | public string Notes { get; set; } 10 | } -------------------------------------------------------------------------------- /CH02/HashCode/HashCodeTest.cs: -------------------------------------------------------------------------------- 1 | public class HashCodeTest 2 | { 3 | public int Halue { get; set; } 4 | 5 | public override bool Equals(object obj) 6 | { 7 | return obj is HashCodeTest test && 8 | Halue == test.Halue; 9 | } 10 | 11 | public override int GetHashCode() 12 | { 13 | return 387336856 + Halue.GetHashCode(); 14 | } 15 | } -------------------------------------------------------------------------------- /CH02/Strings/Locales.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | class Locales 6 | { 7 | public bool isGif(string fileName) 8 | { 9 | return fileName.ToLower().EndsWith(".gif"); 10 | } 11 | public bool isGifFast(string fileName) 12 | { 13 | return fileName.EndsWith(".gif", StringComparison.OrdinalIgnoreCase); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CH02/Arrays/Arrays.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Arrays; 4 | 5 | public class Arrays 6 | { 7 | public static int ArrayVsList() 8 | { 9 | var a = new int[] { 1, 2, 3, 4 }; 10 | int sum1 = a[0] + a[1] + a[2] + a[3]; 11 | var b = new List(new int[] { 1, 2, 3, 4 }); 12 | int sum2 = b[0] + b[1] + b[2] + b[2]; 13 | return sum1 + sum2; 14 | } 15 | } -------------------------------------------------------------------------------- /CH02/Supercalifragilisticexpialidocious/Supercalifragilisticexpialidocious.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Twistat.Pages; 5 | 6 | public class IndexModel : PageModel 7 | { 8 | private readonly ILogger logger; 9 | 10 | public IndexModel(ILogger logger) 11 | { 12 | this.logger = logger; 13 | } 14 | 15 | public void OnGet() 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /CH02/HashCode/ProperGetHashCode.cs: -------------------------------------------------------------------------------- 1 | internal class ProperGetHashCode 2 | { 3 | public int TopicId { get; set; } // both id's are auto-incrementing 4 | public int EntryId { get; set; } // only unique per topic, starting at 1 each time 5 | 6 | public override int GetHashCode() 7 | { 8 | return (int)(((TopicId & 0xFFFF) << 16) 9 | ^ (TopicId & (0xFFFF0000 >> 16)) 10 | ^ EntryId); 11 | } 12 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace FoobleXSS.Pages; 5 | 6 | public class PrivacyModel : PageModel 7 | { 8 | private readonly ILogger logger; 9 | 10 | public PrivacyModel(ILogger logger) 11 | { 12 | this.logger = logger; 13 | } 14 | 15 | public void OnGet() 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/csrf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome to a super secret web site! 5 | 6 |

Welcome to a super secret web site!

7 |

Please click on the button to continue

8 |
9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Twistat.Pages; 5 | 6 | public class PrivacyModel : PageModel 7 | { 8 | private readonly ILogger logger; 9 | 10 | public PrivacyModel(ILogger logger) 11 | { 12 | this.logger = logger; 13 | } 14 | 15 | public void OnGet() 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /CH05/Blabber.Models/Blabber.Models.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 7.3 5 | disable 6 | 1701;1702;IDE0130 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /CH06/LoginExample/UserEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace LoginExample; 5 | 6 | [Table("users")] 7 | public class UserEntity 8 | { 9 | [Key] 10 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | [Column("id")] 12 | public int Id { get; set; } 13 | 14 | [Column("username")] 15 | public string Username { get; set; } = null!; 16 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Models/ShipmentAddress.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | public class ShipmentAddress 4 | { 5 | public string FirstName { get; set; } 6 | public string LastName { get; set; } 7 | public string Address1 { get; set; } 8 | public string Address2 { get; set; } 9 | public string City { get; set; } 10 | 11 | [RegularExpression(@"^\s*\d{5}(-\d{4})?\s*$")] 12 | public string ZipCode { get; set; } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /CH03/HttpHandler/TempReader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Exe 6 | disable 7 | 1701;1702;IDE0130 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CH06/XSS/Pages/Login.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model FoobleXSS.Pages.LoginModel 3 |
4 | @Html.DisplayFor(m => m.ErrorMessage) 5 |
6 | @Html.LabelFor(m => m.Username) 7 |
8 |
9 | @Html.TextBoxFor(m => m.Username) 10 |
11 |
12 | @Html.LabelFor(m => m.Password) 13 |
14 |
15 | @Html.PasswordFor(m => m.Password) 16 |
17 | 18 |
-------------------------------------------------------------------------------- /CH02/HashCode/HashCode.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Library 5 | false 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CH07/Patterns/Boolean.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace Patterns; 7 | class Boolean 8 | { 9 | public bool CheckIfThingsFail(int x) 10 | { 11 | if (verySlowCheck() && x > 5) 12 | { 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | private bool verySlowCheck() 19 | { 20 | Thread.Sleep(1000); 21 | return true; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CH07/Patterns/Patterns.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Exe 6 | true 7 | disable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CH07/IO/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System.Diagnostics; 3 | using static IO.FileCopy; 4 | 5 | namespace IO; 6 | class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | //Trace.Listeners.Add(new DefaultTraceListener()); 11 | var suite = new FileCopyBenchmark(); 12 | suite.Setup(); 13 | suite.CopyAsyncOld(256 * 1024); 14 | suite.CopyAsyncOld(256 * 1024); 15 | BenchmarkRunner.Run(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CH08/Connections/Connections.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Exe 5 | disable 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CH07/Patterns/Badge.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Patterns; 5 | public class Badge 6 | { 7 | public string Name; 8 | public int Level; 9 | 10 | private static ISet visibleBadgeNames = getVisibleBadgeNames(); 11 | 12 | public bool IsVisible 13 | { 14 | get => visibleBadgeNames.Contains(this.Name); 15 | } 16 | 17 | private static ISet getVisibleBadgeNames() 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CH07/Patterns/BufferProcessorBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Patterns; 4 | public class BufferProcessorBenchmark 5 | { 6 | public static int[] Buffer = new int[10_000_000]; 7 | 8 | [Benchmark(Baseline = true)] 9 | public void MultiplyEachClassic() 10 | { 11 | BufferProcessor.MultiplyEachClassic(Buffer, 2); 12 | } 13 | 14 | [Benchmark] 15 | public void MultiplyEachSIMD() 16 | { 17 | BufferProcessor.MultiplyEachSIMD(Buffer, 2); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /CH02/ReferenceVsValueTypes/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ReferenceVsValueTypes; 5 | 6 | internal class Program 7 | { 8 | public static void Main() 9 | { 10 | int result = 5; 11 | var date = new DateTime(1984, 10, 9); 12 | var builder = new StringBuilder(); 13 | string formula = "2 + 2 = "; 14 | builder.Append(formula); 15 | builder.Append(result); 16 | builder.Append(date.ToString()); 17 | Console.WriteLine(builder.ToString()); 18 | } 19 | } -------------------------------------------------------------------------------- /CH06/XSS/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace XSS; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } -------------------------------------------------------------------------------- /CH03/Twistat/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Twistat; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } -------------------------------------------------------------------------------- /CH05/DI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | var b = new B(); 4 | var a = new A(b); 5 | a.X(); 6 | 7 | public interface IB 8 | { 9 | void Y(); 10 | } 11 | 12 | public class A 13 | { 14 | private readonly IB b; 15 | 16 | public A(IB b) 17 | { 18 | this.b = b; 19 | } 20 | 21 | public void X() 22 | { 23 | Console.WriteLine("X got called"); 24 | b.Y(); 25 | } 26 | } 27 | 28 | public class B : IB 29 | { 30 | public void Y() 31 | { 32 | Console.WriteLine("Y got called"); 33 | } 34 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace EmojiChat; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } -------------------------------------------------------------------------------- /CH06/LoginExample/LoginExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CH09/Exceptions/Exceptions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace BlabberCore; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model HomepageModel 2 | @{ 3 | ViewBag.Title = "Blab!"; 4 | } 5 | 6 |

Blabber

7 | 8 | @using (Html.BeginForm("Post", "Blab", FormMethod.Post)) 9 | { 10 | @Html.ValidationMessageFor(m => m.Form) 11 | @Html.EditorFor(m => m.Form) 12 | 13 | 14 | } 15 | 16 |
17 | @if (Model?.Blabs is null || !(Model.Blabs.Any())) 18 | { 19 |

No blabs yet!

20 | } 21 | else 22 | { 23 | @Html.DisplayFor(m => m.Blabs) 24 | } 25 |
-------------------------------------------------------------------------------- /CH05/Blabber/DB/BlabberContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | 3 | namespace Blabber.DB 4 | { 5 | public class BlabberContext : DbContext 6 | { 7 | public BlabberContext(string connectionString) 8 | : base(connectionString) 9 | { 10 | Database.SetInitializer(null); 11 | } 12 | 13 | protected override void OnModelCreating(DbModelBuilder builder) 14 | { 15 | base.OnModelCreating(builder); 16 | } 17 | 18 | public DbSet Blabs { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace FoobleXSS.Pages; 6 | 7 | public class IndexModel : PageModel 8 | { 9 | private readonly ILogger logger; 10 | 11 | public string? Query { get; set; } 12 | 13 | public IndexModel(ILogger logger) 14 | { 15 | this.logger = logger; 16 | } 17 | 18 | public void OnGet([Bind(Prefix = "q")] string? query) 19 | { 20 | this.Query = query; 21 | } 22 | } -------------------------------------------------------------------------------- /CH07/Patterns/StringController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Patterns; 7 | class StringController : Controller 8 | { 9 | public IActionResult Bozo() 10 | { 11 | HttpContext.Items["Bozo"] = true; 12 | return View(); 13 | } 14 | 15 | public IActionResult IsBozo() 16 | { 17 | if ((bool?)HttpContext.Items["Bozo"] == true) 18 | { 19 | return View("Bozo"); 20 | } 21 | return View("NotBozo"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CH05/Blabber/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model HomepageModel 2 | @{ 3 | ViewBag.Title = "Blab!"; 4 | } 5 | 6 |

Blabber

7 | 8 | @using (Html.BeginForm("Post", "Blab", FormMethod.Post)) 9 | { 10 | @Html.ValidationMessageFor(m => m.Form) 11 | @Html.EditorFor(m => m.Form) 12 | 13 | 14 | } 15 | 16 |
17 | @if (Model?.Blabs is null || !(Model.Blabs.Any())) 18 | { 19 |

No blabs yet!

20 | } 21 | else 22 | { 23 | @Html.DisplayFor(m => m.Blabs) 24 | } 25 |
-------------------------------------------------------------------------------- /CH05/Blabber/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace Blabber 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | _ = routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /CH02/Algorithms/Structs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Algorithms; 4 | 5 | internal class Structs 6 | { 7 | private struct Point 8 | { 9 | public int X; 10 | public int Y; 11 | 12 | public override readonly string ToString() => $"X:{X},Y:{Y}"; 13 | } 14 | 15 | public static void Main() 16 | { 17 | var a = new Point() 18 | { 19 | X = 5, 20 | Y = 5, 21 | }; 22 | var b = a; 23 | b.X = 100; 24 | b.Y = 200; 25 | Console.WriteLine(b); 26 | Console.WriteLine(a); 27 | } 28 | } -------------------------------------------------------------------------------- /CH05/Blabber.Models/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Blabber.Models 4 | { 5 | public static class DateTimeExtensions 6 | { 7 | public static string ToIntervalString( 8 | this TimeSpan interval) 9 | { 10 | return interval.TotalHours >= 1.0 11 | ? $"{(int)interval.TotalHours}h" 12 | : interval.TotalMinutes >= 1.0 13 | ? $"{(int)interval.TotalMinutes}m" 14 | : interval.TotalSeconds >= 1.0 15 | ? $"{(int)interval.TotalSeconds}s" : "now"; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /CH09/Exceptions/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Exceptions; 4 | internal interface IDatabase 5 | { 6 | Order GetOrder(Guid orderId); 7 | void ChangeOrderStatus(Order order, OrderStatus newState); 8 | 9 | /// 10 | /// Atomic change of order status and fail if state was 11 | /// changed by some other caller. 12 | /// 13 | bool TryChangeOrderStatus(Order order, 14 | OrderStatus from, 15 | OrderStatus to); 16 | VotingResult Upvote(Guid contentId); 17 | bool ProcessOrder(Order order); 18 | object CreateAlbum(string title); 19 | } -------------------------------------------------------------------------------- /CH06/LoginExample/UserEF.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System.Linq; 3 | 4 | namespace LoginExample; 5 | 6 | internal class UserEF 7 | { 8 | private readonly UserContext dataContext; 9 | 10 | public UserEF(UserContext dataContext) 11 | { 12 | this.dataContext = dataContext; 13 | } 14 | 15 | public int? GetUserId(string username) 16 | { 17 | return dataContext.Users 18 | .FromSqlInterpolated( 19 | $@"SELECT * FROM users WHERE username={username}") 20 | .Select(u => (int?)u.Id) 21 | .FirstOrDefault(); 22 | } 23 | } -------------------------------------------------------------------------------- /CH09/InfiniteLoop/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InfiniteLoop; 4 | class Program 5 | { 6 | public static void Main(string[] args) 7 | { 8 | Console.WriteLine("This app runs in an infinite loop"); 9 | Console.WriteLine("It consumes a lot of CPU too!"); 10 | Console.WriteLine("Press Ctrl-C to quit"); 11 | var rnd = new Random(); 12 | infiniteLoopAggressive(rnd.NextDouble()); 13 | } 14 | 15 | private static void infiniteLoopAggressive(double x) 16 | { 17 | while (true) 18 | { 19 | x *= 13; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH08/Connections/UniqueIdGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace Connections; 4 | 5 | class UniqueIdGenerator 6 | { 7 | private int value; 8 | public int GetNextValue() => ++value; 9 | } 10 | class UniqueIdGeneratorAtomic 11 | { 12 | private int value; 13 | public int GetNextValue() => Interlocked.Increment(ref value); 14 | } 15 | class UniqueIdGeneratorLock 16 | { 17 | private int value; 18 | private object valueLock = new object(); 19 | public int GetNextValue() 20 | { 21 | lock (valueLock) 22 | { 23 | return ++value; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CH05/Blabber.Models/Blab.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Blabber 4 | { 5 | public class Blab 6 | { 7 | public const int MaxContentLength = 560; 8 | 9 | public string Content { get; private set; } 10 | public DateTimeOffset CreatedOn { get; private set; } 11 | 12 | public Blab(string content, DateTimeOffset createdOn) 13 | { 14 | if (string.IsNullOrWhiteSpace(content)) 15 | { 16 | throw new ArgumentException(nameof(content)); 17 | } 18 | Content = content; 19 | CreatedOn = createdOn; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /CH03/HttpHandler/HttpStatusCodeClass.cs: -------------------------------------------------------------------------------- 1 | internal class HttpStatusCodeClass 2 | { 3 | public const int OK = 200; 4 | public const int NotFound = 404; 5 | public const int ServerError = 500; 6 | // ... and so on 7 | } 8 | 9 | internal class ImageWidthsClass 10 | { 11 | public const int Small = 50; 12 | public const int Medium = 100; 13 | public const int Large = 200; 14 | } 15 | 16 | internal enum HttpStatusCodeEnum 17 | { 18 | OK = 200, 19 | NotFound = 404, 20 | ServerError = 500, 21 | } 22 | 23 | internal enum ImageWidthsEnum 24 | { 25 | Small = 50, 26 | Medium = 100, 27 | Large = 200, 28 | } -------------------------------------------------------------------------------- /CH09/Exceptions/Int.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Exceptions; 4 | class Int 5 | { 6 | public static int ParseDefault(string input, 7 | int defaultValue) 8 | { 9 | try 10 | { 11 | return int.Parse(input); 12 | } 13 | catch (FormatException) 14 | { 15 | return defaultValue; 16 | } 17 | } 18 | 19 | public static int ParseDefault2(string input, 20 | int defaultValue) 21 | { 22 | if (!int.TryParse(input, out int result)) 23 | { 24 | return defaultValue; 25 | } 26 | return result; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CH07/BenchmarkRunner/SampleBenchmarkSuite.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | 4 | namespace SimpleBenchmarkRunner; 5 | public class SampleBenchmarkSuite 6 | { 7 | [Params(1000)] 8 | public int A; 9 | 10 | [Params(35)] 11 | public int B; 12 | 13 | [Benchmark] 14 | public int Manual() 15 | { 16 | int division = A / B; 17 | int remainder = A % B; 18 | return division + remainder; 19 | } 20 | 21 | [Benchmark] 22 | public int DivRem() 23 | { 24 | int division = Math.DivRem(A, B, out int remainder); 25 | return division + remainder; 26 | } 27 | } -------------------------------------------------------------------------------- /CH02/ValidationContext/Username.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ValidationContext; 4 | 5 | public class Username 6 | { 7 | public const int MaxLength = 50; // have username 8 | 9 | public string Value { get; } 10 | 11 | public Username(string value) 12 | { 13 | value = value.Trim(); // normalize spaces 14 | if (String.IsNullOrEmpty(value) 15 | || value.Length < 1 16 | || value.Length > MaxLength) 17 | { 18 | throw new ArgumentException("Invalid username", nameof(value)); 19 | } 20 | this.Value = value; 21 | } 22 | 23 | public override string ToString() => Value; 24 | } -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /CH03/Twistat/Twistat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Twistat 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CH08/Connections/AsyncCounter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Connections; 6 | public sealed class AsyncCounter : IDisposable 7 | { 8 | private int value; 9 | private SemaphoreSlim semaphore 10 | = new(initialCount: 0, maxCount: 1); 11 | 12 | public void Dispose() 13 | { 14 | semaphore?.Dispose(); 15 | semaphore = null; 16 | } 17 | 18 | public async Task GetNextValue() 19 | { 20 | int returnValue; 21 | await semaphore.WaitAsync(); 22 | returnValue = ++value; 23 | semaphore.Release(); 24 | return returnValue; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CH04/DateUtilsTests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 1701;1702;IDE0130 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CH07/Patterns/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System; 3 | 4 | namespace Patterns; 5 | public class Program 6 | { 7 | public static void Main(string[] args) 8 | { 9 | //BranchPrediction.CheckFive(5); 10 | //Luhn.CheckUnsafe("1234567890"); 11 | //BenchmarkRunner.Run(); 12 | //var suite = new ChecksumBenchmark(); 13 | //suite.CalculateChecksum(new byte[123]); 14 | //suite.CalculateChecksumParallel(new byte[123]); 15 | //BenchmarkRunner.Run(); 16 | BenchmarkRunner.Run(); 17 | //BenchmarkRunner.Run(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CH05/Blabber.Models/BlabStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Blabber.Models 4 | { 5 | public interface IBlabDb 6 | { 7 | IEnumerable GetAllBlabs(); 8 | 9 | void AddBlab(Blab blab); 10 | } 11 | 12 | public class BlabStorage 13 | { 14 | private readonly IBlabDb db; 15 | 16 | public BlabStorage(IBlabDb db) 17 | { 18 | this.db = db; 19 | } 20 | 21 | public IEnumerable GetAllBlabs() 22 | { 23 | return db.GetAllBlabs(); 24 | } 25 | 26 | public void Add(Blab blab) 27 | { 28 | db.AddBlab(blab); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CH07/Patterns/User.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Patterns; 5 | public class User 6 | { 7 | private IDb db; 8 | 9 | public IEnumerable GetBadgeNames() 10 | { 11 | var badges = db.GetBadges(); 12 | foreach (var badge in badges) 13 | { 14 | if (badge.IsVisible) 15 | { 16 | yield return badge.Name; 17 | } 18 | } 19 | } 20 | 21 | public IEnumerable GetBadgesNames2() 22 | { 23 | var badges = db.GetBadges(); 24 | return badges 25 | .Where(b => b.IsVisible) 26 | .Select(b => b.Name); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CH06/XSS/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Home page"; 5 | } 6 | 7 |
8 |

Welcome to Fooble

9 |

10 | Fooble is the ultimate useless search engine that returns nothing. 11 |

12 | @if (Model.Query is object) { 13 |

14 | Your search for "@Model.Query" 15 | didn't return any results. 16 |

17 | } 18 |
19 | 20 | 21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /CH08/Connections/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Connections; 8 | static class Program 9 | { 10 | public static async Task Main(string[] args) 11 | { 12 | Console.WriteLine("Download all URLs given in command line in parallel"); 13 | var uriArgs = args.Select(s => new Uri(s)); 14 | // download everything in parallel 15 | var result = await ParallelWeb.DownloadAll(uriArgs); 16 | foreach (var pair in result) 17 | { 18 | Console.WriteLine($"{pair.Key,-40}: {pair.Value.Substring(0, 40)}..."); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CH09/Exceptions/ImageAlbum.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Exceptions; 7 | public class AlbumForm 8 | { 9 | public string Title { get; set; } 10 | public IList Files { get; set; } 11 | } 12 | 13 | public class Album 14 | { 15 | public Guid Id { get; set; } 16 | public string Title { get; set; } 17 | } 18 | 19 | public class ImageAlbum : Controller 20 | { 21 | //private IDatabase db; 22 | 23 | //[HttpPost] 24 | //public IActionResult UploadNaive(AlbumForm form) { 25 | // var album = db.CreateAlbum(form.Title); 26 | // //uploadImage(album.Id, ) 27 | //} 28 | } 29 | -------------------------------------------------------------------------------- /CH07/Patterns/ChecksumBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | 4 | namespace Patterns; 5 | public class ChecksumBenchmark 6 | { 7 | public byte[] InitBuffer => getBuffer(); 8 | 9 | private static byte[] getBuffer() 10 | { 11 | var buffer = new byte[1_000_000]; 12 | //var rnd = new Random(); 13 | //rnd.NextBytes(buffer); 14 | return buffer; 15 | } 16 | 17 | [Benchmark] 18 | public int CalculateChecksum() 19 | { 20 | return Checksum.CalculateChecksum(InitBuffer); 21 | } 22 | 23 | [Benchmark] 24 | public int CalculateChecksumParallel() 25 | { 26 | return Checksum.CalculateChecksumParallel(InitBuffer); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CH07/Patterns/UserPreferences.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace Patterns; 4 | public struct UserPreferences 5 | { 6 | public byte ItemsPerPage; 7 | public byte NumberOfItemsOnTheHomepage; 8 | public byte NumberOfAdClicksICanStomach; 9 | public byte MaxNumberOfTrollsInADay; 10 | public byte NumberOfCookiesIAmWillingToAccept; 11 | public byte NumberOfSpamEmailILoveToGetPerDay; 12 | } 13 | 14 | public struct UserPreferences2 15 | { 16 | public int ItemsPerPage; 17 | public int NumberOfItemsOnTheHomepage; 18 | public int NumberOfAdClicksICanStomach; 19 | public int MaxNumberOfTrollsInADay; 20 | public int NumberOfCookiesIAmWillingToAccept; 21 | public int NumberOfSpamEmailILoveToGetPerDay; 22 | } 23 | -------------------------------------------------------------------------------- /CH05/BlabberCore/DB/BlabEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Blabber.DB; 6 | 7 | [Table("Blabs")] 8 | public class BlabEntity 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public Guid Id { get; set; } 13 | 14 | [StringLength(Blab.MaxContentLength)] 15 | [Required] 16 | public string Content { get; set; } 17 | 18 | /// 19 | /// We store DateTime instead of DateTimeOffset 20 | /// because SQLite doesn't support it. Instead, we 21 | /// convert to UTC back & forth to handle different timezones. 22 | /// 23 | [Required] 24 | public DateTime CreatedOn { get; set; } 25 | } -------------------------------------------------------------------------------- /CH05/Blabber/DB/BlabEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace Blabber.DB 6 | { 7 | [Table("Blabs")] 8 | public class BlabEntity 9 | { 10 | [Key] 11 | public Guid Id { get; set; } 12 | 13 | [StringLength(Blab.MaxContentLength)] 14 | [Required] 15 | public string Content { get; set; } 16 | 17 | /// 18 | /// We store DateTime instead of DateTimeOffset 19 | /// because SQLite doesn't support it. Instead, we 20 | /// convert to UTC back & forth to handle different timezones. 21 | /// 22 | [Required] 23 | public DateTime CreatedOn { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /CH08/Connections/ApiTokensLockFree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace Connections; 5 | class ApiTokensLockFree 6 | { 7 | private ConcurrentDictionary tokens { get; } = new(); 8 | 9 | public void Set(string key, Token value) 10 | { 11 | tokens[key] = value; 12 | } 13 | 14 | public Token Get(string key) 15 | { 16 | if (!tokens.TryGetValue(key, out Token value)) 17 | { 18 | value = getTokenFromDb(key); 19 | tokens[key] = value; 20 | return tokens[key]; 21 | } 22 | return value; 23 | } 24 | 25 | private Token getTokenFromDb(string key) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | using System.Diagnostics; 5 | 6 | namespace Twistat.Pages; 7 | 8 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 9 | public class ErrorModel : PageModel 10 | { 11 | public string RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | this.logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | using System.Diagnostics; 5 | 6 | namespace FoobleXSS.Pages; 7 | 8 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 9 | public class ErrorModel : PageModel 10 | { 11 | public string? RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | this.logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } -------------------------------------------------------------------------------- /CH06/XSS/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49286", 7 | "sslPort": 44322 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "XSS": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52320", 7 | "sslPort": 44316 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Shoppidy": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CH05/Blabber/DefaultDependencyResolver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Web.Mvc; 5 | 6 | namespace Blabber 7 | { 8 | public class DefaultDependencyResolver : IDependencyResolver 9 | { 10 | public DefaultDependencyResolver(ServiceProvider provider) 11 | { 12 | Provider = provider; 13 | } 14 | 15 | public ServiceProvider Provider { get; } 16 | 17 | public object GetService(Type serviceType) 18 | { 19 | return Provider.GetService(serviceType); 20 | } 21 | 22 | public IEnumerable GetServices(Type serviceType) 23 | { 24 | return Provider.GetServices(serviceType); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/Controllers/BlabController.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System; 4 | 5 | namespace Blabber.Controllers; 6 | 7 | public class BlabController : Controller 8 | { 9 | private readonly BlabStorage storage; 10 | 11 | public BlabController(BlabStorage storage) 12 | { 13 | this.storage = storage; 14 | } 15 | 16 | private ActionResult home() 17 | { 18 | return RedirectToAction("Index", "Home"); 19 | } 20 | 21 | public ActionResult Post(BlabForm form) 22 | { 23 | if (!ModelState.IsValid) 24 | { 25 | return home(); 26 | } 27 | 28 | var blab = new Blab(form.Content, DateTimeOffset.Now); 29 | storage.Add(blab); 30 | return home(); 31 | } 32 | } -------------------------------------------------------------------------------- /CH08/Connections/ParallelWeb.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace Connections; 8 | public static class ParallelWeb 9 | { 10 | public static async Task> 11 | DownloadAll(IEnumerable uris) 12 | { 13 | var runningTasks = new Dictionary>(); 14 | var client = new HttpClient(); 15 | foreach (var uri in uris) 16 | { 17 | var task = client.GetStringAsync(uri); 18 | runningTasks.Add(uri, task); 19 | } 20 | await Task.WhenAll(runningTasks.Values); 21 | return runningTasks.ToDictionary(kp => kp.Key, 22 | kp => kp.Value.Result); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:58367", 7 | "sslPort": 44348 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "BlabberCore": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Callback.cshtml.cs: -------------------------------------------------------------------------------- 1 | using CoreTweet; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Twistat.Pages; 7 | 8 | public class CallbackModel : PageModel 9 | { 10 | public IEnumerable Nicks { get; set; } 11 | 12 | public void OnGet() 13 | { 14 | // https://localhost:44304/Callback?oauth_token=XhaguQAAAAABFEzEAAABcqR3JuQ&oauth_verifier=focVY8eYIvceblH3rNtPRNrEOtOUP0Pv 15 | var session = (OAuth.OAuthSession)TempData["session"]; 16 | session.GetTokens(Request.Query["oauth_verifier"].First()); 17 | var tokens = new Tokens() 18 | { 19 | }; 20 | Nicks = tokens.Followers.EnumerateList(EnumerateMode.Next) 21 | .Select(e => e.Name); 22 | } 23 | } -------------------------------------------------------------------------------- /CH04/DateUtilsTests/Program.cs: -------------------------------------------------------------------------------- 1 | #if !DEBUG 2 | #error Debug.Asserts will only run in Debug configuration 3 | #endif 4 | 5 | using System; 6 | using System.Diagnostics; 7 | 8 | namespace DateUtilsTests; 9 | 10 | public class Program 11 | { 12 | public static void Main_() 13 | { 14 | var span = TimeSpan.FromSeconds(3); 15 | Debug.Assert(span.ToIntervalString() == "3s", "3s case failed"); 16 | span = TimeSpan.FromMinutes(5); 17 | Debug.Assert(span.ToIntervalString() == "5m", "5m case failed"); 18 | span = TimeSpan.FromHours(7); 19 | Debug.Assert(span.ToIntervalString() == "7h", "7h case failed"); 20 | span = TimeSpan.FromMilliseconds(1); // less than a second 21 | Debug.Assert(span.ToIntervalString() == "now", "now case failed"); 22 | } 23 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Shoppidy 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CH05/Blabber/Controllers/BlabController.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using System; 3 | using System.Web.Mvc; 4 | 5 | namespace Blabber.Controllers 6 | { 7 | public class BlabController : Controller 8 | { 9 | private readonly BlabStorage storage; 10 | 11 | public BlabController(BlabStorage storage) 12 | { 13 | this.storage = storage; 14 | } 15 | 16 | private ActionResult home() 17 | { 18 | return RedirectToAction("Index", "Home"); 19 | } 20 | 21 | public ActionResult Post(BlabForm form) 22 | { 23 | if (!ModelState.IsValid) 24 | { 25 | return home(); 26 | } 27 | var blab = new Blab(form.Content, DateTimeOffset.Now); 28 | storage.Add(blab); 29 | return home(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "command": "dotnet", 9 | "type": "shell", 10 | "args": [ 11 | "build", 12 | // Ask dotnet build to generate full paths for file names. 13 | "/property:GenerateFullPaths=true", 14 | // Do not generate summary otherwise it leads to duplicate errors in Problems panel 15 | "/consoleloggerparameters:NoSummary" 16 | ], 17 | "group": "build", 18 | "presentation": { 19 | "reveal": "silent" 20 | }, 21 | "problemMatcher": "$msCompile" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /CH02/ValidationContext/ParameterTypes.cs: -------------------------------------------------------------------------------- 1 | public struct TopicId 2 | { 3 | public int Id { get; private set; } 4 | 5 | public TopicId(int id) 6 | { 7 | this.Id = id; 8 | } 9 | } 10 | 11 | partial class ParameterTypes 12 | { 13 | public int Move(int from, int to) 14 | { 15 | // ... some cryptic code here 16 | return 0; 17 | } 18 | 19 | public int MoveContents(int fromTopicId, int toTopicId) 20 | { 21 | // ... some cryptic code here 22 | return 0; 23 | } 24 | 25 | public MoveResult MoveContentsB(int fromTopicId, int toTopicId) 26 | { 27 | // ... still quite a code here 28 | return MoveResult.Success; 29 | } 30 | 31 | public MoveResult MoveContentsC(TopicId from, TopicId to) 32 | { 33 | // ... still quite a code here 34 | return MoveResult.Success; 35 | } 36 | } -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Login.cshtml.cs: -------------------------------------------------------------------------------- 1 | using CoreTweet; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace Twistat.Pages; 7 | 8 | public class LoginModel : PageModel 9 | { 10 | private readonly IConfiguration config; 11 | 12 | public LoginModel(IConfiguration configuration) 13 | { 14 | this.config = configuration; 15 | } 16 | 17 | public IActionResult OnGet() 18 | { 19 | string consumerKey = config["Twitter:ConsumerKey"]; 20 | string consumerSecret = config["Twitter:ConsumerSecret"]; 21 | var session = OAuth.Authorize(consumerKey, consumerSecret, 22 | oauthCallback: $"{Request.Scheme}://{Request.Host}/Callback"); 23 | TempData["session"] = session; 24 | return Redirect(session.AuthorizeUri.AbsoluteUri); 25 | } 26 | } -------------------------------------------------------------------------------- /CH06/LoginExample/User.cs: -------------------------------------------------------------------------------- 1 | using System.Data.SqlClient; 2 | 3 | namespace LoginExample; 4 | 5 | public class User 6 | { 7 | private readonly SqlConnection db; 8 | 9 | public User(SqlConnection db) 10 | { 11 | this.db = db; 12 | } 13 | 14 | public int? GetUserId_naive(string username) 15 | { 16 | var cmd = db.CreateCommand(); 17 | cmd.CommandText = @" 18 | SELECT id 19 | FROM users 20 | WHERE name='" + username + "'"; 21 | return cmd.ExecuteScalar() as int?; 22 | } 23 | 24 | public int? GetUserId_parameterized(string username) 25 | { 26 | var cmd = db.CreateCommand(); 27 | cmd.CommandText = @" 28 | SELECT id 29 | FROM users 30 | WHERE name=@username"; 31 | cmd.Parameters.AddWithValue("username", username); 32 | return cmd.ExecuteScalar() as int?; 33 | } 34 | } -------------------------------------------------------------------------------- /CH08/Connections/CpuBound.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Connections; 9 | class CpuBound 10 | { 11 | public async Task LongWork() 12 | { 13 | await Task.Run(() => computeMeaningOfLifeUniverseAndEverything()); 14 | } 15 | 16 | private ManualResetEvent completionEvent = new(initialState: false); 17 | public void LongWorkThread() 18 | { 19 | ThreadPool.QueueUserWorkItem(state => 20 | { 21 | computeMeaningOfLifeUniverseAndEverything(); 22 | completionEvent.Set(); 23 | }); 24 | completionEvent.WaitOne(); 25 | } 26 | 27 | private void computeMeaningOfLifeUniverseAndEverything() 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CH08/Connections/LimitedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Connections; 4 | class LimitedList 5 | { 6 | private List items = new(); 7 | 8 | public LimitedList(int limit) 9 | { 10 | Limit = limit; 11 | } 12 | 13 | public bool Add(T item) 14 | { 15 | if (items.Count >= Limit) 16 | { 17 | return false; 18 | } 19 | lock (items) 20 | { 21 | if (items.Count >= Limit) 22 | { 23 | return false; 24 | } 25 | items.Add(item); 26 | return true; 27 | } 28 | } 29 | 30 | public bool Remove(T item) 31 | { 32 | lock (items) 33 | { 34 | return items.Remove(item); 35 | } 36 | } 37 | 38 | public int Count => items.Count; 39 | public int Limit { get; } 40 | } 41 | -------------------------------------------------------------------------------- /CH05/BlabberCore/DB/BlabDb.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Blabber.DB; 7 | 8 | public class BlabDb : IBlabDb 9 | { 10 | private readonly BlabberContext db; 11 | 12 | public BlabDb(BlabberContext db) 13 | { 14 | this.db = db; 15 | } 16 | 17 | public void AddBlab(Blab blab) 18 | { 19 | db.Blabs.Add(new BlabEntity() 20 | { 21 | Content = blab.Content, 22 | CreatedOn = blab.CreatedOn.UtcDateTime, 23 | }); 24 | db.SaveChanges(); 25 | } 26 | 27 | public IEnumerable GetAllBlabs() 28 | { 29 | return db.Blabs 30 | .OrderByDescending(b => b.CreatedOn) 31 | .Select(b => new Blab(b.Content, 32 | new DateTimeOffset(b.CreatedOn, TimeSpan.Zero))) 33 | .ToList(); 34 | } 35 | } -------------------------------------------------------------------------------- /CH02/ValidationContext/PostId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class PostId : IEquatable 4 | { 5 | public int Value { get; private set; } 6 | 7 | public PostId(int id) 8 | { 9 | if (id <= 0) 10 | { 11 | throw new ArgumentOutOfRangeException(nameof(id)); 12 | } 13 | Value = id; 14 | } 15 | 16 | public override string ToString() => Value.ToString(); 17 | 18 | public override int GetHashCode() => Value; 19 | 20 | public override bool Equals(object obj) 21 | { 22 | return Equals(obj as PostId); 23 | } 24 | 25 | public bool Equals(PostId other) => other?.Value == Value; 26 | 27 | public static bool operator ==(PostId a, PostId b) 28 | { 29 | return a.Equals(b); 30 | } 31 | 32 | public static bool operator !=(PostId a, PostId b) 33 | { 34 | return !a.Equals(b); 35 | } 36 | } -------------------------------------------------------------------------------- /CH03/Twistat/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:56797", 7 | "sslPort": 44304 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Twistat": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /CH05/Blabber/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | 26 | .blab { 27 | font-size: 150%; 28 | width: auto; 29 | display: block; 30 | } 31 | 32 | .blab > .date { 33 | text-align: right; 34 | display: block; 35 | } 36 | 37 | .blabeditor { 38 | display: block; 39 | width: 100%; 40 | margin: 5px; 41 | } 42 | -------------------------------------------------------------------------------- /CH07/Patterns/BranchPrediction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Patterns; 6 | class BranchPrediction 7 | { 8 | public static void CheckFive(int x) 9 | { 10 | if (x == 5) 11 | { 12 | Console.WriteLine("X is five!"); 13 | } 14 | else 15 | { 16 | Console.WriteLine("X is something else"); 17 | } 18 | } 19 | 20 | /* 21 | compare x with 5 22 | branch to LBL_ELSE if not equal 23 | write "X is five" 24 | LBL_ELSE: 25 | write "X is something else" 26 | */ 27 | 28 | /* 29 | cmp ecx, 0x5 30 | jnz L0015 31 | mov ecx, [0xf59d8cc] 32 | call System.Console.WriteLine(System.String) 33 | ret 34 | L0015: mov ecx, [0xf59d8d0] 35 | call System.Console.WriteLine(System.String) 36 | ret 37 | 38 | */ 39 | } 40 | -------------------------------------------------------------------------------- /CH05/BlabberCore/wwwroot/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | 26 | .blab { 27 | font-size: 150%; 28 | width: auto; 29 | display: block; 30 | } 31 | 32 | .blab > .date { 33 | text-align: right; 34 | display: block; 35 | } 36 | 37 | .blabeditor { 38 | display: block; 39 | width: 100%; 40 | margin: 5px; 41 | } 42 | -------------------------------------------------------------------------------- /CH08/Connections/ApiTokens.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Connections; 5 | class ApiTokens 6 | { 7 | private Dictionary tokens { get; } = new(); 8 | 9 | public void Set(string key, Token value) 10 | { 11 | lock (tokens) 12 | { 13 | tokens[key] = value; 14 | } 15 | } 16 | 17 | public Token Get(string key) 18 | { 19 | lock (tokens) 20 | { 21 | if (!tokens.TryGetValue(key, out Token value)) 22 | { 23 | value = getTokenFromDb(key); 24 | tokens[key] = value; 25 | return tokens[key]; 26 | } 27 | return value; 28 | } 29 | } 30 | 31 | private Token getTokenFromDb(string key) 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | } 36 | 37 | internal class Token 38 | { 39 | } 40 | -------------------------------------------------------------------------------- /CH07/Patterns/Checksum.cs: -------------------------------------------------------------------------------- 1 | namespace Patterns; 2 | 3 | class Checksum 4 | { 5 | public static int CalculateChecksum(byte[] array) 6 | { 7 | int result = 0; 8 | for (int i = 0; i < array.Length; i++) 9 | { 10 | result += array[i]; 11 | } 12 | return result; 13 | } 14 | 15 | public static int CalculateChecksumParallel(byte[] array) 16 | { 17 | int r0 = 0, r1 = 0, r2 = 0, r3 = 0; 18 | int len = array.Length; 19 | int i = 0; 20 | for (; i < len - 4; i += 4) 21 | { 22 | r0 += array[i + 0]; 23 | r1 += array[i + 1]; 24 | r2 += array[i + 2]; 25 | r3 += array[i + 3]; 26 | } 27 | int remainingSum = 0; 28 | for (; i < len; i++) 29 | { 30 | remainingSum += array[i]; 31 | } 32 | return r0 + r1 + r2 + r3 + remainingSum; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CH05/Blabber/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization; 2 | 3 | namespace Blabber 4 | { 5 | public class BundleConfig 6 | { 7 | // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 8 | public static void RegisterBundles(BundleCollection bundles) 9 | { 10 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 | "~/Scripts/jquery-{version}.js")); 12 | 13 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 14 | "~/Scripts/jquery.validate*")); 15 | 16 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 17 | "~/Scripts/bootstrap.js")); 18 | 19 | bundles.Add(new StyleBundle("~/Content/css").Include( 20 | "~/Content/bootstrap.css", 21 | "~/Content/site.css")); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /CH06/XSS/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /CH07/Patterns/MemberAccessBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Patterns; 4 | public class MemberAccessBenchmark 5 | { 6 | public UserPreferences prefs; 7 | public UserPreferences2 prefs2; 8 | 9 | [Benchmark] 10 | public int ByteMemberAccess() 11 | { 12 | return prefs.ItemsPerPage 13 | + prefs.NumberOfItemsOnTheHomepage 14 | + prefs.NumberOfAdClicksICanStomach 15 | + prefs.MaxNumberOfTrollsInADay 16 | + prefs.NumberOfCookiesIAmWillingToAccept 17 | + prefs.NumberOfSpamEmailILoveToGetPerDay; 18 | } 19 | 20 | [Benchmark] 21 | public int IntMemberAccess() 22 | { 23 | return prefs2.ItemsPerPage 24 | + prefs2.NumberOfItemsOnTheHomepage 25 | + prefs2.NumberOfAdClicksICanStomach 26 | + prefs2.MaxNumberOfTrollsInADay 27 | + prefs2.NumberOfCookiesIAmWillingToAccept 28 | + prefs2.NumberOfSpamEmailILoveToGetPerDay; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CH03/OrderAPI/Shipping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Shipping 4 | { 5 | private readonly IDatabase db; 6 | 7 | public void SetShippingAddress(Guid customerId, 8 | PostalAddress newAddress) 9 | { 10 | normalizeFields(newAddress); 11 | db.UpdateShippingAddress(customerId, newAddress); 12 | } 13 | 14 | private void normalizeFields(PostalAddress address) 15 | { 16 | address.FirstName = TextHelper.Capitalize(address.FirstName); 17 | address.LastName = TextHelper.Capitalize(address.LastName); 18 | } 19 | 20 | private void normalizeFields2(PostalAddress address) 21 | { 22 | address.FirstName = TextHelper.Capitalize(address.FirstName); 23 | address.LastName = TextHelper.Capitalize(address.LastName); 24 | address.City = TextHelper.Capitalize(address.City); 25 | } 26 | } 27 | 28 | internal interface IDatabase 29 | { 30 | void UpdateShippingAddress(Guid customerId, PostalAddress newAddress); 31 | } -------------------------------------------------------------------------------- /CH04/DateUtilsTests/UsernameTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using User; 4 | 5 | namespace Tests; 6 | 7 | internal class UsernameTest 8 | { 9 | [Test] 10 | public void ctor_nullUsername_ThrowsArgumentNullException() 11 | { 12 | Assert.Throws( 13 | () => new Username(null)); 14 | } 15 | 16 | [TestCase("")] 17 | [TestCase("Upper")] 18 | [TestCase("toolongusername")] 19 | [TestCase("root!!")] 20 | [TestCase("a b")] 21 | public void ctor_invalidUsername_ThrowsArgumentException(string username) 22 | { 23 | Assert.Throws( 24 | () => new Username(username)); 25 | } 26 | 27 | [TestCase("a")] 28 | [TestCase("1")] 29 | [TestCase("hunter2")] 30 | [TestCase("12345678")] 31 | [TestCase("abcdefgh")] 32 | public void ctor_validUsername_DoesNotThrow(string username) 33 | { 34 | Assert.DoesNotThrow(() => new Username(username)); 35 | } 36 | } -------------------------------------------------------------------------------- /CH02/Strings/StringExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | public class StringExample 5 | { 6 | public static string JoinNames(string[] names) 7 | { 8 | string result = String.Empty; 9 | int lastIndex = names.Length - 1; 10 | for (int i = 0; i < lastIndex; i++) 11 | { 12 | result += names[i] + ", "; 13 | } 14 | result += names[lastIndex]; 15 | return result; 16 | } 17 | 18 | public static string JoinNamesSB(string[] names) 19 | { 20 | var builder = new StringBuilder(); 21 | int lastIndex = names.Length - 1; 22 | for (int i = 0; i < lastIndex; i++) 23 | { 24 | builder.Append(names[i]); 25 | builder.Append(", "); 26 | } 27 | builder.Append(names[lastIndex]); 28 | return builder.ToString(); 29 | } 30 | 31 | public static string JoinNames2(string[] names) 32 | { 33 | return String.Join(", ", names); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Migrations/20201117054127_Initial.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using System; 3 | 4 | namespace Blabber.Migrations; 5 | 6 | public partial class Initial : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "Blabs", 12 | columns: table => new 13 | { 14 | Id = table.Column(type: "TEXT", nullable: false), 15 | Content = table.Column(type: "TEXT", maxLength: 560, nullable: false), 16 | CreatedOn = table.Column(type: "TEXT", nullable: false) 17 | }, 18 | constraints: table => 19 | { 20 | table.PrimaryKey("PK_Blabs", x => x.Id); 21 | }); 22 | } 23 | 24 | protected override void Down(MigrationBuilder migrationBuilder) 25 | { 26 | migrationBuilder.DropTable( 27 | name: "Blabs"); 28 | } 29 | } -------------------------------------------------------------------------------- /CH09/Exceptions/Voting.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | 4 | namespace Exceptions; 5 | public enum VotingResult 6 | { 7 | Success, 8 | ContentTooOld, 9 | ContentDeleted, 10 | } 11 | 12 | public class Voting : Controller 13 | { 14 | private readonly IDatabase db; 15 | 16 | [HttpPost] 17 | public IActionResult Upvote(Guid contentId) 18 | { 19 | var result = db.Upvote(contentId); 20 | return result switch 21 | { 22 | VotingResult.Success 23 | => success(), 24 | VotingResult.ContentTooOld 25 | => warning("Content is too old. It can't be voted"), 26 | VotingResult.ContentDeleted 27 | => warning("Content is deleted. It can't be voted"), 28 | }; 29 | } 30 | 31 | private IActionResult success() 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | private IActionResult warning(string v) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CH06/XSS/Pages/User.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Html; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System; 4 | 5 | namespace FoobleXSS.Pages; 6 | 7 | public class UserModel : PageModel 8 | { 9 | public IHtmlContent? Username { get; set; } 10 | 11 | public void OnGet(string username) 12 | { 13 | bool isActive = isUserActive(username); 14 | var content = new HtmlContentBuilder(); 15 | if (isActive) 16 | { 17 | content.AppendFormat("", username); 18 | } 19 | content.Append(username); 20 | if (isActive) 21 | { 22 | content.AppendHtml(""); 23 | } 24 | Username = content; 25 | } 26 | 27 | private bool isUserActive(string _) 28 | { 29 | return false; 30 | } 31 | 32 | private Range getRowRange(int pageIndex, int pageSize) 33 | { 34 | int startRow = (pageIndex - 1) * pageSize; 35 | int endRow = startRow + pageSize; 36 | return new Range(startRow, endRow); 37 | } 38 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:59464", 8 | "sslPort": 44309 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "EmojiChat": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /CH04/DateUtils/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public static class DateTimeExtensions 4 | { 5 | public static string ToIntervalString( 6 | this TimeSpan interval) 7 | { 8 | if (interval.TotalHours >= 1.0) 9 | { 10 | return $"{(int)interval.TotalHours}h"; 11 | } 12 | if (interval.TotalMinutes >= 1.0) 13 | { 14 | return $"{(int)interval.TotalMinutes}m"; 15 | } 16 | if (interval.TotalSeconds >= 1.0) 17 | { 18 | return $"{(int)interval.TotalSeconds}s"; 19 | } 20 | return "now"; 21 | } 22 | 23 | public static bool IsLegalBirthdate(DateTime birthdate) 24 | { 25 | const int legalAge = 18; 26 | var now = DateTime.Now; 27 | int age = now.Year - birthdate.Year; 28 | if (age == legalAge) 29 | { 30 | return now.Month > birthdate.Month 31 | || (now.Month == birthdate.Month 32 | && now.Day > birthdate.Day); 33 | } 34 | return age > legalAge; 35 | } 36 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Logging; 8 | using Shoppidy.Models; 9 | 10 | namespace Shoppidy.Controllers 11 | { 12 | public class HomeController : Controller 13 | { 14 | private readonly ILogger logger; 15 | 16 | public HomeController(ILogger logger) 17 | { 18 | this.logger = logger; 19 | } 20 | 21 | public IActionResult Index() 22 | { 23 | return View(); 24 | } 25 | 26 | public IActionResult Privacy() 27 | { 28 | return View(); 29 | } 30 | 31 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 32 | public IActionResult Error() 33 | { 34 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CH07/Patterns/BufferProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Patterns; 4 | 5 | public class BufferProcessor 6 | { 7 | public static void MultiplyEachSIMD(int[] buffer, int value) 8 | { 9 | if (!Vector.IsHardwareAccelerated) 10 | { 11 | MultiplyEachClassic(buffer, value); 12 | return; 13 | } 14 | 15 | int chunkSize = Vector.Count; 16 | int n = 0; 17 | for (; n < buffer.Length - chunkSize; n += chunkSize) 18 | { 19 | var vector = new Vector(buffer, n); 20 | vector *= value; // multiply all values in the vector at once 21 | vector.CopyTo(buffer, n); // replace the values 22 | } 23 | 24 | // process remaining bytes 25 | for (; n < buffer.Length; n++) 26 | { 27 | buffer[n] *= value; 28 | } 29 | } 30 | 31 | public static void MultiplyEachClassic(int[] buffer, int value) 32 | { 33 | for (int n = 0; n < buffer.Length; n++) 34 | { 35 | buffer[n] *= value; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Linq; 4 | 5 | namespace Blabber.Controllers; 6 | 7 | public class HomeController : Controller 8 | { 9 | private static readonly HomepageModel defaultHomepageModel = new HomepageModel 10 | { 11 | Blabs = Enumerable.Empty(), 12 | Form = new BlabForm() 13 | { 14 | Content = null, 15 | } 16 | }; 17 | 18 | private readonly BlabStorage storage; 19 | 20 | public HomeController(BlabStorage storage) 21 | { 22 | this.storage = storage; 23 | } 24 | 25 | public ActionResult Index() 26 | { 27 | var model = defaultHomepageModel; 28 | model.Blabs = storage.GetAllBlabs(); 29 | return View(model); 30 | } 31 | 32 | public ActionResult About() 33 | { 34 | ViewBag.Message = "Your application description page."; 35 | 36 | return View(); 37 | } 38 | 39 | public ActionResult Contact() 40 | { 41 | ViewBag.Message = "Your contact page."; 42 | 43 | return View(); 44 | } 45 | } -------------------------------------------------------------------------------- /CH03/Twistat/Twitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | public class Twitter 5 | { 6 | public static Uri GetAuthorizationUrl(Uri callbackUrl) 7 | { 8 | string redirectUrl = ""; 9 | // do something here to build the redirect url 10 | return new Uri(redirectUrl); 11 | } 12 | 13 | public static TwitterAccessToken GetAccessToken( 14 | TwitterCallbackInfo callbackData) 15 | { 16 | // we should be getting something like this 17 | return new TwitterAccessToken(); 18 | } 19 | 20 | public Twitter(TwitterAccessToken accessToken) 21 | { 22 | // we should store this somewhere 23 | } 24 | 25 | public IEnumerable GetListOfFollowers( 26 | TwitterUserId userId) 27 | { 28 | // no idea how this will work 29 | yield break; 30 | } 31 | } 32 | 33 | public class TwitterUserId 34 | { 35 | // who knows how twitter defines user ids 36 | } 37 | 38 | public class TwitterAccessToken 39 | { 40 | // no idea what this will be 41 | } 42 | 43 | public class TwitterCallbackInfo 44 | { 45 | // this neither 46 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace EmojiChat.Controllers; 8 | 9 | [ApiController] 10 | [Route("[controller]")] 11 | public class WeatherForecastController : ControllerBase 12 | { 13 | private static readonly string[] summaries = new[] { 14 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 15 | }; 16 | 17 | private readonly ILogger logger; 18 | 19 | public WeatherForecastController(ILogger logger) 20 | { 21 | this.logger = logger; 22 | } 23 | 24 | [HttpGet] 25 | public IEnumerable Get() 26 | { 27 | var rng = new Random(); 28 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 29 | { 30 | Date = DateTime.Now.AddDays(index), 31 | TemperatureC = rng.Next(-20, 55), 32 | Summary = summaries[rng.Next(summaries.Length)] 33 | }) 34 | .ToArray(); 35 | } 36 | } -------------------------------------------------------------------------------- /CH05/Blabber/DB/BlabDb.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Blabber.DB 7 | { 8 | public class BlabDb : IBlabDb 9 | { 10 | private readonly BlabberContext db; 11 | 12 | public BlabDb(BlabberContext db) 13 | { 14 | this.db = db; 15 | } 16 | 17 | public void AddBlab(Blab blab) 18 | { 19 | db.Blabs.Add(new BlabEntity() 20 | { 21 | // SQLite provider for .NET Framework doesn't 22 | // support auto generated GUID identifiers. 23 | Id = Guid.NewGuid(), 24 | Content = blab.Content, 25 | CreatedOn = blab.CreatedOn.UtcDateTime, 26 | }); 27 | db.SaveChanges(); 28 | } 29 | 30 | public IEnumerable GetAllBlabs() 31 | { 32 | return db.Blabs 33 | .OrderByDescending(b => b.CreatedOn) 34 | .ToList() 35 | .Select(b => new Blab(b.Content, 36 | new DateTimeOffset(b.CreatedOn, TimeSpan.Zero))) 37 | .ToList(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH08/Connections/Cache.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace Connections; 4 | public class Cache 5 | { 6 | private static object instanceLock = new object(); 7 | private static Cache instance; 8 | public static Cache Instance 9 | { 10 | get 11 | { 12 | lock (instanceLock) 13 | { 14 | if (instance is null) 15 | { 16 | instance = new Cache(); 17 | } 18 | return instance; 19 | } 20 | } 21 | } 22 | public static Cache Instance2 23 | { 24 | get 25 | { 26 | if (instance is not null) 27 | { 28 | return instance; 29 | } 30 | lock (instanceLock) 31 | { 32 | if (instance is null) 33 | { 34 | instance = new Cache(); 35 | } 36 | return instance; 37 | } 38 | } 39 | } 40 | 41 | public static Cache Instance3 42 | { 43 | get 44 | { 45 | return LazyInitializer.EnsureInitialized(ref instance); 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH02/ValidationContext/DbId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class DbId 4 | { 5 | public int Value { get; private set; } 6 | 7 | public DbId(int id) 8 | { 9 | if (id <= 0) 10 | { 11 | throw new ArgumentOutOfRangeException(nameof(id)); 12 | } 13 | Value = id; 14 | } 15 | 16 | public override string ToString() => Value.ToString(); 17 | 18 | public override int GetHashCode() => Value; 19 | 20 | public override bool Equals(object obj) 21 | { 22 | return obj is DbId other && other.Value == Value; 23 | } 24 | 25 | public static bool operator ==(DbId a, DbId b) 26 | { 27 | return a.Equals(b); 28 | } 29 | 30 | public static bool operator !=(DbId a, DbId b) 31 | { 32 | return !a.Equals(b); 33 | } 34 | 35 | public class PostId : DbId 36 | { 37 | public PostId(int id) : base(id) 38 | { 39 | } 40 | } 41 | 42 | public class TopicId : DbId 43 | { 44 | public TopicId(int id) : base(id) 45 | { 46 | } 47 | } 48 | 49 | public class UserId : DbId 50 | { 51 | public UserId(int id) : base(id) 52 | { 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CH05/Blabber/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Models; 2 | using System.Linq; 3 | using System.Web.Mvc; 4 | 5 | namespace Blabber.Controllers 6 | { 7 | public class HomeController : Controller 8 | { 9 | public HomeController(BlabStorage storage) 10 | { 11 | this.storage = storage; 12 | } 13 | 14 | private static readonly HomepageModel defaultHomepageModel = new HomepageModel 15 | { 16 | Blabs = Enumerable.Empty(), 17 | Form = new BlabForm() 18 | { 19 | Content = null, 20 | } 21 | }; 22 | 23 | private readonly BlabStorage storage; 24 | 25 | public ActionResult Index() 26 | { 27 | var model = defaultHomepageModel; 28 | model.Blabs = storage.GetAllBlabs(); 29 | return View(model); 30 | } 31 | 32 | public ActionResult About() 33 | { 34 | ViewBag.Message = "Your application description page."; 35 | 36 | return View(); 37 | } 38 | 39 | public ActionResult Contact() 40 | { 41 | ViewBag.Message = "Your contact page."; 42 | 43 | return View(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /CH02/Nullability/DbId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class DbId : IEquatable 4 | { 5 | public int Value { get; private set; } 6 | 7 | public DbId(int id) 8 | { 9 | if (id <= 0) 10 | { 11 | throw new ArgumentOutOfRangeException(nameof(id)); 12 | } 13 | Value = id; 14 | } 15 | 16 | public override string ToString() => Value.ToString(); 17 | 18 | public override bool Equals(object obj) 19 | { 20 | return Equals(obj as DbId); 21 | } 22 | 23 | public bool Equals(DbId other) => other?.Value == Value; 24 | 25 | public override int GetHashCode() 26 | { 27 | return -1937169414 + Value.GetHashCode(); 28 | } 29 | 30 | public static bool operator ==(DbId a, DbId b) 31 | { 32 | return a.Equals(b); 33 | } 34 | 35 | public static bool operator !=(DbId a, DbId b) 36 | { 37 | return !a.Equals(b); 38 | } 39 | } 40 | 41 | public class EntryId : DbId 42 | { 43 | public EntryId(int id) : base(id) 44 | { 45 | } 46 | } 47 | 48 | public class TopicId : DbId 49 | { 50 | public TopicId(int id) : base(id) 51 | { 52 | } 53 | } 54 | 55 | public class UserId : DbId 56 | { 57 | public UserId(int id) : base(id) 58 | { 59 | } 60 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace EmojiChat; 8 | 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddControllers(); 22 | } 23 | 24 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 25 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 26 | { 27 | if (env.IsDevelopment()) 28 | { 29 | app.UseDeveloperExceptionPage(); 30 | } 31 | 32 | app.UseHttpsRedirection(); 33 | 34 | app.UseRouting(); 35 | 36 | app.UseAuthorization(); 37 | 38 | app.UseEndpoints(endpoints => 39 | { 40 | endpoints.MapControllers(); 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /CH03/EmojiChat/Controllers/StatsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | using System.Data.SqlClient; 4 | 5 | public class UserStats 6 | { 7 | public int Received { get; set; } 8 | public int Sent { get; set; } 9 | } 10 | 11 | [Route("stats/{action}")] 12 | public class StatsController : ControllerBase 13 | { 14 | private readonly IConfiguration config; 15 | 16 | public StatsController(IConfiguration config) 17 | { 18 | this.config = config; 19 | } 20 | 21 | [HttpGet] 22 | public UserStats Get(int userId) 23 | { 24 | var result = new UserStats(); 25 | string connectionString = config.GetConnectionString("DB"); 26 | using (var conn = new SqlConnection(connectionString)) 27 | { 28 | conn.Open(); 29 | var cmd = conn.CreateCommand(); 30 | cmd.CommandText = 31 | "SELECT COUNT(*) FROM Messages WHERE FromId={0}"; 32 | cmd.Parameters.Add(userId); 33 | result.Sent = (int)cmd.ExecuteScalar(); 34 | cmd.CommandText = 35 | "SELECT COUNT(*) FROM Messages WHERE ToId={0}"; 36 | result.Received = (int)cmd.ExecuteScalar(); 37 | } 38 | return result; 39 | } 40 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/Migrations/BlabberContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Blabber.DB; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | namespace Blabber.Migrations; 9 | 10 | [DbContext(typeof(BlabberContext))] 11 | partial class BlabberContextModelSnapshot : ModelSnapshot 12 | { 13 | protected override void BuildModel(ModelBuilder modelBuilder) 14 | { 15 | #pragma warning disable 612, 618 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "5.0.0"); 18 | 19 | modelBuilder.Entity("Blabber.DB.BlabEntity", b => 20 | { 21 | b.Property("Id") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("TEXT"); 24 | 25 | b.Property("Content") 26 | .IsRequired() 27 | .HasMaxLength(560) 28 | .HasColumnType("TEXT"); 29 | 30 | b.Property("CreatedOn") 31 | .HasColumnType("TEXT"); 32 | 33 | b.HasKey("Id"); 34 | 35 | b.ToTable("Blabs"); 36 | }); 37 | #pragma warning restore 612, 618 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CH04/User/Username.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace User; 5 | 6 | public class Username 7 | { 8 | public string Value { get; private set; } 9 | private const string validUsernamePattern = @"^[a-z0-9]{1,8}$"; 10 | 11 | public Username(string username) 12 | { 13 | if (username is null) 14 | { 15 | throw new ArgumentNullException(nameof(username)); 16 | } 17 | if (!Regex.IsMatch(username, validUsernamePattern)) 18 | { 19 | throw new ArgumentException("Invalid username", 20 | nameof(username)); 21 | } 22 | this.Value = username; 23 | } 24 | 25 | public override string ToString() => base.ToString(); 26 | 27 | public override int GetHashCode() => Value.GetHashCode(); 28 | 29 | public override bool Equals(object obj) 30 | { 31 | return obj is Username other && other.Value == Value; 32 | } 33 | 34 | public static implicit operator string(Username username) 35 | { 36 | return username.Value; 37 | } 38 | 39 | public static bool operator ==(Username a, Username b) 40 | { 41 | return a.Value == b.Value; 42 | } 43 | 44 | public static bool operator !=(Username a, Username b) 45 | { 46 | return !(a == b); 47 | } 48 | } -------------------------------------------------------------------------------- /CH04/DateUtilsTests/FrameworkTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | 4 | namespace DateUtilsTests; 5 | 6 | internal class DateTimeExtensionsTests 7 | { 8 | [TestCase("00:00:03.000", ExpectedResult = "3s")] 9 | [TestCase("00:05:00.000", ExpectedResult = "5m")] 10 | [TestCase("07:00:00.000", ExpectedResult = "7h")] 11 | [TestCase("00:00:00.001", ExpectedResult = "now")] 12 | public string ToIntervalString_ReturnsExpectedValues( 13 | string timeSpanText) 14 | { 15 | var input = TimeSpan.Parse(timeSpanText); 16 | return input.ToIntervalString(); 17 | } 18 | 19 | [TestCase(18, 0, -1, ExpectedResult = true)] 20 | [TestCase(18, 0, 0, ExpectedResult = false)] 21 | [TestCase(18, 0, 1, ExpectedResult = false)] 22 | [TestCase(18, -1, 0, ExpectedResult = true)] 23 | [TestCase(18, 1, 0, ExpectedResult = false)] 24 | [TestCase(19, 0, 0, ExpectedResult = true)] 25 | [TestCase(17, 0, 0, ExpectedResult = false)] 26 | public bool IsLegalBirthdate_ReturnsExpectedValues( 27 | int yearDifference, int monthDifference, int dayDifference) 28 | { 29 | var now = DateTime.Now; 30 | var input = now.AddYears(-yearDifference) 31 | .AddMonths(monthDifference) 32 | .AddDays(dayDifference); 33 | return DateTimeExtensions.IsLegalBirthdate(input); 34 | } 35 | } -------------------------------------------------------------------------------- /CH05/Blabber/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Migrations/20201117054127_Initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Blabber.DB; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace Blabber.Migrations; 10 | 11 | [DbContext(typeof(BlabberContext))] 12 | [Migration("20201117054127_Initial")] 13 | partial class Initial 14 | { 15 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "5.0.0"); 20 | 21 | modelBuilder.Entity("Blabber.DB.BlabEntity", b => 22 | { 23 | b.Property("Id") 24 | .ValueGeneratedOnAdd() 25 | .HasColumnType("TEXT"); 26 | 27 | b.Property("Content") 28 | .IsRequired() 29 | .HasMaxLength(560) 30 | .HasColumnType("TEXT"); 31 | 32 | b.Property("CreatedOn") 33 | .HasColumnType("TEXT"); 34 | 35 | b.HasKey("Id"); 36 | 37 | b.ToTable("Blabs"); 38 | }); 39 | #pragma warning restore 612, 618 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /CH07/BenchmarkRunner/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using BenchmarkDotNet.Running; 4 | 5 | namespace SimpleBenchmarkRunner; 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | BenchmarkRunner.Run(); 11 | runBenchmarks(); 12 | } 13 | 14 | private const int iterations = 1_000_000_000; 15 | 16 | private static void runBenchmarks() 17 | { 18 | var suite = new SampleBenchmarkSuite 19 | { 20 | A = 1000, 21 | B = 35 22 | }; 23 | 24 | long manualTime = runBenchmark(() => suite.Manual()); 25 | long divRemTime = runBenchmark(() => suite.DivRem()); 26 | 27 | reportResult("Manual", manualTime); 28 | reportResult("DivRem", divRemTime); 29 | } 30 | 31 | private static long runBenchmark(Func action) 32 | { 33 | var watch = Stopwatch.StartNew(); 34 | for (int n = 0; n < iterations; n++) 35 | { 36 | action(); 37 | } 38 | watch.Stop(); 39 | return watch.ElapsedMilliseconds; 40 | } 41 | 42 | private static void reportResult(string name, long milliseconds) 43 | { 44 | double nanoseconds = milliseconds * 1000000; 45 | Console.WriteLine("{0} = {1}ns / operation", 46 | name, 47 | nanoseconds / iterations); 48 | } 49 | } -------------------------------------------------------------------------------- /CH05/Blabber/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /CH05/BlabberCore/BlabberCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Blabber 6 | disable 7 | 1701;1702;IDE0130;NETSDK1206 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | all 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /CH05/Blabber/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Blabber")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Blabber")] 12 | [assembly: AssemblyCopyright("Copyright © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("0cef8ab9-4bc7-4b14-ba61-19582d773695")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /CH02/HashCode/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("HashCode")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("HashCode")] 12 | [assembly: AssemblyCopyright("Copyright © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("7d152ee0-acfa-42c1-b50a-df6814eaac57")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /CH08/Connections/Post.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Connections; 8 | public class MismatchedPostCount 9 | { 10 | public Guid UserId { get; set; } 11 | public int Count { get; set; } 12 | public int ActualCount { get; set; } 13 | } 14 | 15 | class Post 16 | { 17 | private IDatabase db; 18 | private Guid userId; 19 | 20 | public Guid UserId { get; set; } 21 | 22 | public void AddPost(PostContent content) 23 | { 24 | using (var transaction = db.BeginTransaction()) 25 | { 26 | db.InsertPost(content); 27 | int postCount = db.GetPostCount(userId); 28 | postCount++; 29 | db.UpdatePostCount(userId, postCount); 30 | } 31 | } 32 | 33 | public void UpdateAllPostCounts() 34 | { 35 | var inconsistentCounts = db.GetMismatchedPostCounts(); 36 | foreach (var entry in inconsistentCounts) 37 | { 38 | db.UpdatePostCount(entry.UserId, entry.ActualCount); 39 | } 40 | } 41 | } 42 | 43 | internal interface IDatabase 44 | { 45 | IDisposable BeginTransaction(); 46 | int GetPostCount(Guid userId); 47 | void InsertPost(PostContent content); 48 | IQueryable Query(); 49 | IQueryable GetMismatchedPostCounts(); 50 | void UpdatePostCount(Guid userId, int postCount); 51 | } 52 | 53 | public class PostContent 54 | { 55 | } 56 | -------------------------------------------------------------------------------- /CH06/XSS/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace XSS; 8 | 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddRazorPages(); 22 | } 23 | 24 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 25 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 26 | { 27 | if (env.IsDevelopment()) 28 | { 29 | app.UseDeveloperExceptionPage(); 30 | } 31 | else 32 | { 33 | app.UseExceptionHandler("/Error"); 34 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 35 | app.UseHsts(); 36 | } 37 | 38 | app.UseHttpsRedirection(); 39 | app.UseStaticFiles(); 40 | 41 | app.UseRouting(); 42 | 43 | app.UseAuthorization(); 44 | 45 | app.UseEndpoints(endpoints => 46 | { 47 | endpoints.MapRazorPages(); 48 | }); 49 | } 50 | } -------------------------------------------------------------------------------- /CH03/Twistat/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace Twistat; 8 | 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddRazorPages(); 22 | } 23 | 24 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 25 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 26 | { 27 | if (env.IsDevelopment()) 28 | { 29 | app.UseDeveloperExceptionPage(); 30 | } 31 | else 32 | { 33 | app.UseExceptionHandler("/Error"); 34 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 35 | app.UseHsts(); 36 | } 37 | 38 | app.UseHttpsRedirection(); 39 | app.UseStaticFiles(); 40 | 41 | app.UseRouting(); 42 | 43 | app.UseAuthorization(); 44 | 45 | app.UseEndpoints(endpoints => 46 | { 47 | endpoints.MapRazorPages(); 48 | }); 49 | } 50 | } -------------------------------------------------------------------------------- /streetcoder-nowin.slnf: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "path": "streetcoder.sln", 4 | "projects": [ 5 | "CH02\\Algorithms\\Algorithms.csproj", 6 | "CH02\\Arrays\\Arrays.csproj", 7 | "CH02\\HashCode\\HashCode.csproj", 8 | "CH02\\Nullability\\Nullability.csproj", 9 | "CH02\\ReferenceVsValueTypes\\ReferenceVsValueTypes.csproj", 10 | "CH02\\Supercalifragilisticexpialidocious\\Supercalifragilisticexpialidocious.csproj", 11 | "CH02\\ValidationContext\\ValidationContext.csproj", 12 | "CH03\\ClassesVsStructs\\ClassesVsStructs.csproj", 13 | "CH03\\EmojiChat\\EmojiChat.csproj", 14 | "CH03\\HttpHandler\\TempReader.csproj", 15 | "CH03\\IfElse\\IfElse.csproj", 16 | "CH03\\OrderAPI\\OrderAPI.csproj", 17 | "CH03\\Shoppidy\\Shoppidy.csproj", 18 | "CH03\\TechnicalDebt\\TechnicalDebt.csproj", 19 | "CH03\\Twistat\\Twistat.csproj", 20 | "CH04\\DateUtilsTests\\Tests.csproj", 21 | "CH04\\DateUtils\\DateUtils.csproj", 22 | "CH04\\Posts\\Posts.csproj", 23 | "CH04\\Summer\\Summer.csproj", 24 | "CH04\\User\\User.csproj", 25 | "CH05\\Blabber.Models\\Blabber.Models.csproj", 26 | "CH05\\BlabberCore\\BlabberCore.csproj", 27 | "CH05\\DI\\DI.csproj", 28 | "CH06\\LoginExample\\LoginExample.csproj", 29 | "CH06\\XSS\\FoobleXSS.csproj", 30 | "CH07\\BenchmarkRunner\\SimpleBenchmarkRunner.csproj", 31 | "CH07\\IO\\IO.csproj", 32 | "CH07\\Patterns\\Patterns.csproj", 33 | "CH08\\Connections\\Connections.csproj", 34 | "CH09\\Exceptions\\Exceptions.csproj", 35 | "CH09\\InfiniteLoop\\InfiniteLoop.csproj" 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /CH03/HttpHandler/Program.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using RestSharp; 3 | using System; 4 | using System.Net; 5 | 6 | internal static class Program 7 | { 8 | public static int Main(string[] args) 9 | { 10 | if (args.Length != 1) 11 | { 12 | Console.Error.WriteLine(@"Usage: tempreader , 13 | 14 | Example: tempreader 38.66,-121.4004"); 15 | return 1; 16 | } 17 | args = args[0].Split(','); 18 | double lat = double.Parse(args[0].Trim()); 19 | double lon = double.Parse(args[1].Trim()); 20 | double? result = getTemperature(lat, lon); 21 | if (result is null) 22 | { 23 | Console.Error.WriteLine("Failed the API request"); 24 | return 1; 25 | } 26 | Console.WriteLine( 27 | $"Temperature at {lat},{lon} is {result}°F"); 28 | return 0; 29 | } 30 | 31 | private static double? getTemperature(double latitude, 32 | double longitude) 33 | { 34 | const string apiUrl = "https://api.weather.gov"; 35 | string coordinates = $"{latitude},{longitude}"; 36 | string requestPath = $"/points/{coordinates}/forecast/hourly"; 37 | 38 | var client = new RestClient(apiUrl); 39 | var request = new RestRequest(requestPath); 40 | var response = client.Get(request); 41 | if (response.StatusCode == HttpStatusCode.OK) 42 | { 43 | dynamic obj = JObject.Parse(response.Content); 44 | var period = obj.properties.periods[0]; 45 | return (double)period.temperature; 46 | } 47 | return null; 48 | } 49 | } -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Provide sufficient contrast against white background */ 11 | a { 12 | color: #0366d6; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 22 | color: #fff; 23 | background-color: #1b6ec2; 24 | border-color: #1861ac; 25 | } 26 | 27 | /* Sticky footer styles 28 | -------------------------------------------------- */ 29 | html { 30 | font-size: 14px; 31 | } 32 | @media (min-width: 768px) { 33 | html { 34 | font-size: 16px; 35 | } 36 | } 37 | 38 | .border-top { 39 | border-top: 1px solid #e5e5e5; 40 | } 41 | .border-bottom { 42 | border-bottom: 1px solid #e5e5e5; 43 | } 44 | 45 | .box-shadow { 46 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 47 | } 48 | 49 | button.accept-policy { 50 | font-size: 1rem; 51 | line-height: inherit; 52 | } 53 | 54 | /* Sticky footer styles 55 | -------------------------------------------------- */ 56 | html { 57 | position: relative; 58 | min-height: 100%; 59 | } 60 | 61 | body { 62 | /* Margin bottom by footer height */ 63 | margin-bottom: 60px; 64 | } 65 | .footer { 66 | position: absolute; 67 | bottom: 0; 68 | width: 100%; 69 | white-space: nowrap; 70 | line-height: 60px; /* Vertically center the text there */ 71 | } 72 | -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Provide sufficient contrast against white background */ 11 | a { 12 | color: #0366d6; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 22 | color: #fff; 23 | background-color: #1b6ec2; 24 | border-color: #1861ac; 25 | } 26 | 27 | /* Sticky footer styles 28 | -------------------------------------------------- */ 29 | html { 30 | font-size: 14px; 31 | } 32 | @media (min-width: 768px) { 33 | html { 34 | font-size: 16px; 35 | } 36 | } 37 | 38 | .border-top { 39 | border-top: 1px solid #e5e5e5; 40 | } 41 | .border-bottom { 42 | border-bottom: 1px solid #e5e5e5; 43 | } 44 | 45 | .box-shadow { 46 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 47 | } 48 | 49 | button.accept-policy { 50 | font-size: 1rem; 51 | line-height: inherit; 52 | } 53 | 54 | /* Sticky footer styles 55 | -------------------------------------------------- */ 56 | html { 57 | position: relative; 58 | min-height: 100%; 59 | } 60 | 61 | body { 62 | /* Margin bottom by footer height */ 63 | margin-bottom: 60px; 64 | } 65 | .footer { 66 | position: absolute; 67 | bottom: 0; 68 | width: 100%; 69 | white-space: nowrap; 70 | line-height: 60px; /* Vertically center the text there */ 71 | } 72 | -------------------------------------------------------------------------------- /CH05/Blabber/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using Blabber.Controllers; 2 | using Blabber.DB; 3 | using Blabber.Models; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | 9 | namespace Blabber 10 | { 11 | public class MvcApplication : System.Web.HttpApplication 12 | { 13 | private const string connectionString = "Data Source=blabber.db"; 14 | 15 | protected void Application_Start() 16 | { 17 | AreaRegistration.RegisterAllAreas(); 18 | RouteConfig.RegisterRoutes(RouteTable.Routes); 19 | BundleConfig.RegisterBundles(BundleTable.Bundles); 20 | setupDependencyInjection(); 21 | } 22 | 23 | private void setupDependencyInjection() 24 | { 25 | var services = new ServiceCollection(); 26 | configureServices(services, connectionString); 27 | var provider = services.BuildServiceProvider(); 28 | var resolver = new DefaultDependencyResolver(provider); 29 | DependencyResolver.SetResolver(resolver); 30 | } 31 | 32 | private void configureServices(ServiceCollection services, string connectionString) 33 | { 34 | services.AddScoped((svc) => new BlabberContext("db")); 35 | services.AddScoped((svc) => 36 | new BlabDb(svc.GetRequiredService())); 37 | services.AddScoped(); 38 | 39 | // setup controllers 40 | services.AddTransient(); 41 | services.AddTransient(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /CH07/IO/FileCopyBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System.IO; 3 | 4 | namespace IO; 5 | public partial class FileCopy 6 | { 7 | public class FileCopyBenchmark 8 | { 9 | private const long fileSize = 100 * 1024 * 1024; // 100mb 10 | private string sourceFilename; 11 | private string destinationFilename; 12 | 13 | [GlobalSetup] 14 | public void Setup() 15 | { 16 | sourceFilename = Path.GetTempFileName(); 17 | // grow the file 18 | using var stream = File.OpenWrite(sourceFilename); 19 | stream.SetLength(fileSize); 20 | stream.Close(); 21 | destinationFilename = Path.GetTempFileName(); 22 | } 23 | 24 | [Benchmark] 25 | [Arguments(1)] 26 | public void Copy(int bufferSize) 27 | { 28 | FileCopy.Copy(sourceFilename, destinationFilename); 29 | } 30 | 31 | [Benchmark] 32 | //[Arguments(512)] 33 | //[Arguments(1024)] 34 | //[Arguments(16 * 1024)] 35 | [Arguments(256 * 1024)] 36 | //[Arguments(1024 * 1024)] 37 | //[Arguments(2048 * 1024)] 38 | public void CopyBuffered(int bufferSize) 39 | { 40 | FileCopy.CopyBuffered(sourceFilename, 41 | destinationFilename, bufferSize); 42 | } 43 | 44 | [Benchmark] 45 | [Arguments(256 * 1024)] 46 | public void CopyAsyncOld(int bufferSize) 47 | { 48 | var result = FileCopy.CopyAsyncOld(sourceFilename, 49 | destinationFilename, bufferSize); 50 | result.Wait(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CH05/Blabber/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title - Blabber 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 30 |
31 | @RenderBody() 32 |
33 |
34 |

© @DateTime.Now.Year - Manning Publications

35 |
36 |
37 | 38 | @Scripts.Render("~/bundles/jquery") 39 | @Scripts.Render("~/bundles/bootstrap") 40 | @RenderSection("scripts", required: false) 41 | 42 | -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Provide sufficient contrast against white background */ 11 | a { 12 | color: #0366d6; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 22 | color: #fff; 23 | background-color: #1b6ec2; 24 | border-color: #1861ac; 25 | } 26 | 27 | /* Sticky footer styles 28 | -------------------------------------------------- */ 29 | html { 30 | font-size: 14px; 31 | } 32 | 33 | @media (min-width: 768px) { 34 | html { 35 | font-size: 16px; 36 | } 37 | } 38 | 39 | .border-top { 40 | border-top: 1px solid #e5e5e5; 41 | } 42 | 43 | .border-bottom { 44 | border-bottom: 1px solid #e5e5e5; 45 | } 46 | 47 | .box-shadow { 48 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 49 | } 50 | 51 | button.accept-policy { 52 | font-size: 1rem; 53 | line-height: inherit; 54 | } 55 | 56 | /* Sticky footer styles 57 | -------------------------------------------------- */ 58 | html { 59 | position: relative; 60 | min-height: 100%; 61 | } 62 | 63 | body { 64 | /* Margin bottom by footer height */ 65 | margin-bottom: 60px; 66 | } 67 | 68 | .footer { 69 | position: absolute; 70 | bottom: 0; 71 | width: 100%; 72 | white-space: nowrap; 73 | line-height: 60px; /* Vertically center the text there */ 74 | } -------------------------------------------------------------------------------- /CH06/XSS/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /CH02/Algorithms/ContainsAlgorithms.cs: -------------------------------------------------------------------------------- 1 | namespace Algorithms; 2 | 3 | public class ContainsAlgorithms 4 | { 5 | public static bool Contains1(int[] array, int lookFor) 6 | { 7 | for (int n = 0; n < array.Length; n++) 8 | { 9 | if (array[n] == lookFor) 10 | { 11 | return true; 12 | } 13 | } 14 | return false; 15 | } 16 | 17 | public static bool Contains2(int[] array, int lookFor) 18 | { 19 | if (lookFor < 1) 20 | { 21 | return false; 22 | } 23 | for (int n = 0; n < array.Length; n++) 24 | { 25 | if (array[n] == lookFor) 26 | { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | 33 | public static bool Contains3(uint[] array, uint lookFor) 34 | { 35 | for (int n = 0; n < array.Length; n++) 36 | { 37 | if (array[n] == lookFor) 38 | { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | public static bool Contains(uint[] array, uint lookFor) 46 | { 47 | int start = 0; 48 | int end = array.Length - 1; 49 | while (start <= end) 50 | { 51 | int middle = start + ((end - start) / 2); 52 | uint value = array[middle]; 53 | if (lookFor == value) 54 | { 55 | return true; 56 | } 57 | if (lookFor > value) 58 | { 59 | start = middle + 1; 60 | } 61 | else 62 | { 63 | end = middle - 1; 64 | } 65 | } 66 | return false; 67 | } 68 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /CH03/Twistat/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /CH03/ClassesVsStructs/Identifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Class 4 | { 5 | public class Id 6 | { 7 | public int Value { get; private set; } 8 | 9 | public Id(int value) 10 | { 11 | this.Value = value; 12 | } 13 | } 14 | 15 | public class Person 16 | { 17 | public int Id { get; private set; } 18 | public string FirstName { get; private set; } 19 | public string LastName { get; private set; } 20 | public string City { get; private set; } 21 | 22 | public Person(int id, string firstName, string lastName, 23 | string city) 24 | { 25 | Id = id; 26 | FirstName = firstName; 27 | LastName = lastName; 28 | City = city; 29 | } 30 | } 31 | } 32 | 33 | namespace Struct 34 | { 35 | public struct Id 36 | { 37 | public int Value { get; private set; } 38 | 39 | public Id(int value) 40 | { 41 | this.Value = value; 42 | } 43 | 44 | private void testMethod() 45 | { 46 | var a = new Person(42, "Sedat", "Kapanoglu", "San Francisco"); 47 | var b = a; 48 | b.City = "Eskisehir"; 49 | Console.WriteLine(a.City); 50 | Console.WriteLine(b.City); 51 | } 52 | } 53 | 54 | public struct Person 55 | { 56 | public int Id { get; set; } 57 | public string FirstName { get; set; } 58 | public string LastName { get; set; } 59 | public string City { get; set; } 60 | 61 | public Person(int id, string firstName, string lastName, 62 | string city) 63 | { 64 | Id = id; 65 | FirstName = firstName; 66 | LastName = lastName; 67 | City = city; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /CH02/Nullability/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | #nullable enable 4 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 5 | 6 | internal class ConferenceRegistration 7 | { 8 | public string CampaignSource { get; set; } 9 | public string FirstName { get; set; } 10 | public string? MiddleName { get; set; } 11 | public string LastName { get; set; } 12 | public string Email { get; set; } 13 | public DateTimeOffset CreatedOn { get; set; } 14 | } 15 | 16 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 17 | 18 | internal class ConferenceRegistration2 19 | { 20 | public string CampaignSource { get; private set; } 21 | public string FirstName { get; private set; } 22 | public string? MiddleName { get; private set; } 23 | public string LastName { get; private set; } 24 | public string Email { get; private set; } 25 | public DateTimeOffset CreatedOn { get; private set; } = DateTime.Now; 26 | 27 | public ConferenceRegistration2( 28 | string firstName, 29 | string? middleName, 30 | string lastName, 31 | string email, 32 | string? campaignSource = null) 33 | { 34 | FirstName = firstName; 35 | MiddleName = middleName; 36 | LastName = lastName; 37 | Email = email; 38 | CampaignSource = campaignSource ?? "organic"; 39 | } 40 | } 41 | 42 | internal class ConferenceRegistration3 43 | { 44 | public string CampaignSource { get; set; } = "organic"; 45 | public string FirstName { get; set; } = null!; 46 | public string? MiddleName { get; set; } 47 | public string LastName { get; set; } = null!; 48 | public string Email { get; set; } = null!; 49 | public DateTimeOffset CreatedOn { get; set; } 50 | } 51 | 52 | #nullable restore -------------------------------------------------------------------------------- /CH05/BlabberCore/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title - Blabber 7 | 8 | 9 | 10 | 11 | 12 | 31 |
32 | @RenderBody() 33 |
34 |
35 |

© @DateTime.Now.Year - Manning Publications

36 |
37 |
38 | 39 | 40 | 41 | @RenderSection("scripts", required: false) 42 | 43 | 44 | -------------------------------------------------------------------------------- /CH02/Nullability/TopicService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public interface IDatabase 4 | { 5 | bool UpdateEntryTopics(TopicId sourceTopic, TopicId destinationTopic); 6 | 7 | bool IsAlreadyMoved(TopicId from); 8 | } 9 | 10 | public interface IUser 11 | { 12 | bool Authorized(string role); 13 | } 14 | 15 | public class TopicService 16 | { 17 | private IDatabase db; 18 | private IUser user; 19 | 20 | public MoveResult MoveContents(TopicId from, TopicId to) 21 | { 22 | if (from is null) 23 | { 24 | throw new ArgumentNullException(nameof(from)); 25 | } 26 | if (to is null) 27 | { 28 | throw new ArgumentNullException(nameof(to)); 29 | } 30 | if (!user.Authorized("move_contents")) 31 | { 32 | return MoveResult.Unauthorized; 33 | } 34 | if (db.IsAlreadyMoved(from)) 35 | { 36 | return MoveResult.AlreadyMoved; 37 | } 38 | if (db.UpdateEntryTopics(from, to)) 39 | { 40 | return MoveResult.Success; 41 | } 42 | return MoveResult.AlreadyMoved; 43 | } 44 | 45 | #nullable enable 46 | 47 | public MoveResult MoveContentsNullRef(TopicId from, TopicId to) 48 | { 49 | if (db.IsAlreadyMoved(from)) 50 | { 51 | return MoveResult.AlreadyMoved; 52 | } 53 | if (db.UpdateEntryTopics(from, to)) 54 | { 55 | return MoveResult.Success; 56 | } 57 | return MoveResult.AlreadyMoved; 58 | } 59 | 60 | public void Test() 61 | { 62 | #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. 63 | _ = MoveContentsNullRef(null, null); 64 | #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. 65 | } 66 | 67 | #nullable restore 68 | } -------------------------------------------------------------------------------- /CH02/Supercalifragilisticexpialidocious/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using System; 3 | using System.Net; 4 | using System.Text.RegularExpressions; 5 | 6 | internal class Program 7 | { 8 | public static void Main() 9 | { 10 | Console.WriteLine("Extremely simple URL shortener"); 11 | _ = new WebHostBuilder() 12 | .UseUrls("http://localhost:5100", "http://localhost:5101", "http://*:5102") 13 | .Build(); 14 | } 15 | 16 | // URL format is https://supercalifragilisticexpialidocious.io/ 17 | public string GetShortCodeStr(string url) 18 | { 19 | const string urlValidationPattern = @"^https?://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?$"; 20 | if (!Regex.IsMatch(url, urlValidationPattern)) 21 | { 22 | return null; // not a valid URL 23 | } 24 | // take the part after the last slash 25 | string[] parts = url.Split('/'); 26 | string lastPart = parts[^1]; 27 | return lastPart; 28 | } 29 | 30 | // URL format is https://supercalifragilisticexpialidocious.io/ 31 | public string GetShortCodeUri(Uri url) 32 | { 33 | string path = url.AbsolutePath; 34 | if (path.Contains('/')) 35 | { 36 | return null; // don't allow multiple paths 37 | } 38 | return path; 39 | } 40 | 41 | public void IPLoopback() 42 | { 43 | var testAddress = IPAddress.Loopback; 44 | } 45 | 46 | public void BirthDateCalculator() 47 | { 48 | var now = DateTimeOffset.Now; 49 | var birthDate = 50 | new DateTimeOffset(1976, 12, 21, 02, 00, 00, 51 | TimeSpan.FromHours(2)); 52 | TimeSpan timePassed = now - birthDate; 53 | Console.WriteLine($"It’s been {timePassed.TotalSeconds} seconds since I was born!"); 54 | } 55 | } -------------------------------------------------------------------------------- /CH05/BlabberCore/DB/BlabberContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace Blabber.DB; 7 | 8 | public class BlabberContext : DbContext 9 | { 10 | public BlabberContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | } 14 | 15 | protected override void OnModelCreating(ModelBuilder builder) 16 | { 17 | base.OnModelCreating(builder); 18 | 19 | if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite") 20 | { 21 | // SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations 22 | // here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations 23 | // To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset 24 | // use the DateTimeOffsetToBinaryConverter 25 | // Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754 26 | // This only supports millisecond precision, but should be sufficient for most use cases. 27 | foreach (var entityType in builder.Model.GetEntityTypes()) 28 | { 29 | var properties = entityType.ClrType.GetProperties() 30 | .Where(p => p.PropertyType == typeof(DateTimeOffset) 31 | || p.PropertyType == typeof(DateTimeOffset?)); 32 | foreach (var property in properties) 33 | { 34 | builder 35 | .Entity(entityType.Name) 36 | .Property(property.Name) 37 | .HasConversion(new DateTimeOffsetToBinaryConverter()); 38 | } 39 | } 40 | } 41 | } 42 | 43 | public DbSet Blabs { get; set; } 44 | } -------------------------------------------------------------------------------- /CH03/Shoppidy/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.HttpsPolicy; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | 12 | namespace Shoppidy 13 | { 14 | public class Startup 15 | { 16 | public Startup(IConfiguration configuration) 17 | { 18 | Configuration = configuration; 19 | } 20 | 21 | public IConfiguration Configuration { get; } 22 | 23 | // This method gets called by the runtime. Use this method to add services to the container. 24 | public void ConfigureServices(IServiceCollection services) 25 | { 26 | services.AddControllersWithViews(); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 30 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 31 | { 32 | if (env.IsDevelopment()) 33 | { 34 | app.UseDeveloperExceptionPage(); 35 | } 36 | else 37 | { 38 | app.UseExceptionHandler("/Home/Error"); 39 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 40 | app.UseHsts(); 41 | } 42 | app.UseHttpsRedirection(); 43 | app.UseStaticFiles(); 44 | 45 | app.UseRouting(); 46 | 47 | app.UseAuthorization(); 48 | 49 | app.UseEndpoints(endpoints => 50 | { 51 | endpoints.MapControllerRoute( 52 | name: "default", 53 | pattern: "{controller=Home}/{action=Index}/{id?}"); 54 | }); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /CH05/Blabber/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /CH05/BlabberCore/Startup.cs: -------------------------------------------------------------------------------- 1 | using Blabber.DB; 2 | using Blabber.Models; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | 10 | namespace BlabberCore; 11 | 12 | public class Startup 13 | { 14 | private const string connectionString = "Data Source=blabber.db"; 15 | 16 | public Startup(IConfiguration configuration) 17 | { 18 | Configuration = configuration; 19 | } 20 | 21 | public IConfiguration Configuration { get; } 22 | 23 | // This method gets called by the runtime. Use this method to add services to the container. 24 | public void ConfigureServices(IServiceCollection services) 25 | { 26 | services.AddControllersWithViews(); 27 | services.AddDbContext(options => 28 | options.UseSqlite(connectionString)); 29 | services.AddScoped((services) => 30 | new BlabDb( 31 | services.GetRequiredService())); 32 | services.AddScoped(); 33 | } 34 | 35 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 36 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 37 | { 38 | if (env.IsDevelopment()) 39 | { 40 | app.UseDeveloperExceptionPage(); 41 | } 42 | else 43 | { 44 | app.UseExceptionHandler("/Home/Error"); 45 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 46 | app.UseHsts(); 47 | } 48 | app.UseHttpsRedirection(); 49 | app.UseStaticFiles(); 50 | 51 | app.UseRouting(); 52 | 53 | app.UseAuthorization(); 54 | 55 | app.UseEndpoints(endpoints => 56 | { 57 | endpoints.MapControllerRoute( 58 | name: "default", 59 | pattern: "{controller=Home}/{action=Index}/{id?}"); 60 | }); 61 | } 62 | } -------------------------------------------------------------------------------- /CH09/Exceptions/OrderController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | 4 | namespace Exceptions; 5 | public enum OrderStatus 6 | { 7 | New, 8 | Processing, 9 | Complete, 10 | Failed, 11 | } 12 | 13 | public class Order 14 | { 15 | public Guid Id { get; set; } 16 | public OrderStatus Status { get; set; } 17 | public DateTimeOffset LastUpdate { get; set; } 18 | } 19 | 20 | class OrderController : Controller 21 | { 22 | private readonly IDatabase db; 23 | 24 | private static readonly TimeSpan orderTimeout = TimeSpan.FromSeconds(30); 25 | 26 | [HttpPost] 27 | public IActionResult Submit(Guid orderId) 28 | { 29 | Order order = db.GetOrder(orderId); 30 | 31 | if (!db.TryChangeOrderStatus(order, from: OrderStatus.New, 32 | to: OrderStatus.Processing)) 33 | { 34 | if (order.Status != OrderStatus.Processing) 35 | { 36 | return redirectToResultPage(order); 37 | } 38 | 39 | if (DateTimeOffset.Now - order.LastUpdate > orderTimeout) 40 | { 41 | db.ChangeOrderStatus(order, OrderStatus.Failed); 42 | return redirectToResultPage(order); 43 | } 44 | 45 | return orderStatusView(order); 46 | } 47 | 48 | if (!processOrder(order)) 49 | { 50 | db.ChangeOrderStatus(order, OrderStatus.Failed); 51 | } 52 | else 53 | { 54 | db.TryChangeOrderStatus(order, from: OrderStatus.Processing, 55 | to: OrderStatus.Complete); 56 | } 57 | 58 | return redirectToResultPage(order); 59 | } 60 | 61 | private bool processOrder(Order order) 62 | { 63 | if (!db.ProcessOrder(order)) 64 | { 65 | return false; 66 | } 67 | order.Status = OrderStatus.Complete; 68 | return true; 69 | } 70 | 71 | private IActionResult orderStatusView(Order order) 72 | { 73 | return View("OrderStatus", order); 74 | } 75 | 76 | private IActionResult redirectToResultPage(Order order) 77 | { 78 | return RedirectToAction("Result", new { orderId = order.Id }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /CH04/Posts/PostService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Posts; 6 | 7 | public class Tag 8 | { 9 | public Guid Id { get; set; } 10 | public string Title { get; set; } 11 | } 12 | 13 | public class PostService 14 | { 15 | public const int MaxPageSize = 100; 16 | private readonly IPostRepository db; 17 | 18 | public PostService(IPostRepository db) 19 | { 20 | this.db = db; 21 | } 22 | 23 | private IList toListTrimmed(byte numberOfItems, 24 | IQueryable query) 25 | { 26 | return query.Take(numberOfItems).ToList(); 27 | } 28 | 29 | public IList GetTrendingTags(byte numberOfItems) 30 | { 31 | return toListTrimmed(numberOfItems, db.GetTrendingTagTable()); 32 | } 33 | 34 | public IList GetTrendingTagsByTitle( 35 | byte numberOfItems) 36 | { 37 | return toListTrimmed(numberOfItems, db.GetTrendingTagTable() 38 | .OrderBy(p => p.Title)); 39 | } 40 | 41 | public IList GetYesterdaysTrendingTags(byte numberOfItems) 42 | { 43 | return toListTrimmed(numberOfItems, 44 | db.GetYesterdaysTrendingTagTable()); 45 | } 46 | 47 | public IList GetTrendingTags(byte numberOfItems, 48 | bool sortByTitle) 49 | { 50 | var query = db.GetTrendingTagTable(); 51 | if (sortByTitle) 52 | { 53 | query = query.OrderBy(p => p.Title); 54 | } 55 | return query.Take(numberOfItems).ToList(); 56 | } 57 | 58 | public IList GetTrendingTags(byte numberOfItems, 59 | bool sortByTitle, bool yesterdaysTags) 60 | { 61 | var query = yesterdaysTags 62 | ? db.GetTrendingTagTable() 63 | : db.GetYesterdaysTrendingTagTable(); 64 | if (sortByTitle) 65 | { 66 | query = query.OrderBy(p => p.Title); 67 | } 68 | return query.Take(numberOfItems).ToList(); 69 | } 70 | 71 | public Tag GetTagDetails(byte numberOfItems, int index) 72 | { 73 | if (index >= numberOfItems) 74 | { 75 | throw new ArgumentOutOfRangeException(nameof(numberOfItems)); 76 | } 77 | return GetTrendingTags(numberOfItems)[index]; 78 | } 79 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | book cover 3 | 4 | 5 | # about 6 | 7 | This repository contains the example code snippets 8 | referenced in my book [Street Coder](https://streetcoder.org) 9 | published by Manning Publications. 10 | 11 | # requirements 12 | 13 | Almost all projects target the latest .NET LTS release. They targeted .NET 5 originally, 14 | but .NET 5 support ended in early 2022. With this upgrade, you can build the projects 15 | using current tools with as little installation work and warnings as possible. 16 | The source code is still in parity with the book. I plan on maintaining 17 | this repository as easily buildable as possible for readers. 18 | 19 | I also added a new "modern" branch to adopt newer coding conventions like nullable references, 20 | primary constructors, records and so forth. Don't hesitate to check that out to see how 21 | things are done on a newer version of .NET and C#. 22 | 23 | One project requires .NET Framework 4.8 which was required for the migration example. 24 | You can open `streetcoder-nowin.slnf` solution filter to filter it out instead of the main 25 | solution file `streetcoder.sln`. 26 | 27 | You can open and build solution and project files using the latest version 28 | of [Visual Studio](https://visualstudio.microsoft.com/vs/). The Community Edition of Visual Studio 29 | is free for personal use, ~and it's available on Mac too.~ 30 | 31 | You can also use [Visual Studio Code](https://code.visualstudio.com/) with 32 | [OmniSharp C# extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) 33 | if you find it more comfortable. 34 | 35 | # notes 36 | - The different flavors of the same function may have different names in the source 37 | code while they're called the same in the repository. For example, `Contains` 38 | function can have variants like `Contains2` and `Contains3` in the source code, but 39 | it's listed as `Contains` in all the listings with different varieties. That's 40 | intentional to keep the whole source code compilable without any trouble. 41 | Some flavors can use a word-based suffix instead of a numeric one. 42 | 43 | -------------------------------------------------------------------------------- /CH07/Patterns/Luhn.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Patterns; 4 | public class Luhn 5 | { 6 | public static bool CheckNaive(string number) 7 | { 8 | int sum = charToInt(number[^1]); 9 | if (sum < 0) 10 | { 11 | return false; 12 | } 13 | 14 | int len = number.Length; 15 | int parity = len % 2; 16 | 17 | for (int i = 0; i < len - 2; i++) 18 | { 19 | int digit = charToInt(number[i]); 20 | if (digit < 0) 21 | { 22 | return false; 23 | } 24 | if (i % 2 == parity) 25 | { 26 | digit *= 2; 27 | } 28 | if (digit > 9) 29 | { 30 | digit -= 9; 31 | } 32 | sum += digit; 33 | } 34 | return (sum % 10) == 0; 35 | } 36 | 37 | public unsafe static bool CheckUnsafe(string number) 38 | { 39 | int sum = charToInt(number[^1]); 40 | if (sum < 0) 41 | { 42 | return false; 43 | } 44 | 45 | int len = number.Length; 46 | int parity = len % 2; 47 | 48 | for (int i = 0; i < len - 2; i++) 49 | { 50 | int digit = charToInt(number[i]); 51 | if (digit < 0) 52 | { 53 | return false; 54 | } 55 | if (i % 2 == parity) 56 | { 57 | digit *= 2; 58 | } 59 | if (digit > 9) 60 | { 61 | digit -= 9; 62 | } 63 | sum += digit; 64 | } 65 | return (sum % 10) == 0; 66 | } 67 | 68 | private static int charToInt(char c) 69 | { 70 | int result = c - '0'; 71 | if (result < 0 || result > 9) 72 | { 73 | return -1; 74 | } 75 | return result; 76 | } 77 | } 78 | 79 | public class LuhnBenchmark 80 | { 81 | [Params("12345678901234567890")] 82 | public string Number; 83 | 84 | [Benchmark] 85 | public bool CheckNaive() 86 | { 87 | return Luhn.CheckNaive(Number); 88 | } 89 | 90 | [Benchmark] 91 | public bool CheckUnsafe() 92 | { 93 | return Luhn.CheckUnsafe(Number); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CH03/Shoppidy/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - Shoppidy 7 | 8 | 9 | 10 | 11 |
12 | 31 |
32 |
33 |
34 | @RenderBody() 35 |
36 |
37 | 38 |
39 |
40 | © 2020 - Shoppidy - Privacy 41 |
42 |
43 | 44 | 45 | 46 | @RenderSection("Scripts", required: false) 47 | 48 | 49 | -------------------------------------------------------------------------------- /CH02/HashCode/HashCode.csproj.old: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7D152EE0-ACFA-42C1-B50A-DF6814EAAC57} 8 | Library 9 | Properties 10 | HashCode 11 | HashCode 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /CH05/Blabber/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /CH06/XSS/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - Fooble 7 | 8 | 9 | 10 | 11 |
12 | 34 |
35 |
36 |
37 | @RenderBody() 38 |
39 |
40 | 41 |
42 |
43 | © 2021 - Fooble - Privacy 44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | @RenderSection("Scripts", required: false) 52 | 53 | 54 | -------------------------------------------------------------------------------- /CH03/Twistat/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - Twistat 7 | 8 | 9 | 10 | 11 |
12 | 34 |
35 |
36 |
37 | @RenderBody() 38 |
39 |
40 | 41 |
42 |
43 | © 2020 - Twistat - Privacy 44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | @RenderSection("Scripts", required: false) 52 | 53 | 54 | -------------------------------------------------------------------------------- /CH09/Exceptions/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | 6 | namespace Exceptions; 7 | class Program 8 | { 9 | private const string downloadFolder = "app2"; 10 | public static void Main(string[] args) 11 | { 12 | if (args.Length == 0) 13 | { 14 | Console.WriteLine("Usage: update | -selfupdate"); 15 | Environment.Exit(1); 16 | } 17 | 18 | string updatePath = Path.Combine(Path.GetTempPath(), 19 | downloadFolder); 20 | 21 | if (args[0] == "-selfupdate") 22 | { 23 | createUpdateFolder(updatePath); 24 | 25 | Console.WriteLine("Self updating..."); 26 | if (!downloadFiles(updatePath, updateFiles)) 27 | { 28 | Console.WriteLine("Download failed, cleaning up"); 29 | //... 30 | } 31 | } 32 | } 33 | 34 | private static void createUpdateFolder(string updatePath) 35 | { 36 | if (Directory.Exists(updatePath)) 37 | { 38 | Directory.Delete(updatePath); 39 | } 40 | Directory.CreateDirectory(updatePath); 41 | } 42 | 43 | private const string updateServerUriPrefix = 44 | "https://streetcoder.org/selfupdate/"; 45 | 46 | private static readonly string[] updateFiles = 47 | new[] { "Exceptions.exe", "Exceptions.app.config" }; 48 | 49 | private static bool downloadFiles(string directory, 50 | IEnumerable files) 51 | { 52 | foreach (var filename in updateFiles) 53 | { 54 | string path = Path.Combine(directory, filename); 55 | var uri = new Uri(updateServerUriPrefix + filename); 56 | if (!downloadFile(uri, path)) 57 | { 58 | return false; 59 | } 60 | } 61 | return true; 62 | } 63 | 64 | private static bool downloadFile(Uri uri, string path) 65 | { 66 | #pragma warning disable SYSLIB0014 // Type or member is obsolete 67 | // WebClient is old and soon to be obsoleted for making 68 | // HTTP requests. HttpClient should be preferred. 69 | // But it's simple enough for our example here. 70 | using var client = new WebClient(); 71 | try 72 | { 73 | client.DownloadFile(uri, path); 74 | return true; 75 | } 76 | catch (WebException) 77 | { 78 | return false; 79 | } 80 | } 81 | #pragma warning restore SYSLIB0014 // Type or member is obsolete 82 | } 83 | --------------------------------------------------------------------------------