├── .config
└── dotnet-tools.json
├── .gitignore
├── DotNet2020.sln
├── NuGet.config
├── README.md
├── build.ps1
├── build.sh
├── global.json
├── install-sdk.ps1
├── install-sdk.sh
└── src
├── DotNet2020.Host
├── DotNet2020.Host.csproj
├── Extensions
│ └── IHostExtensions.cs
├── Infrastructure
│ ├── Migrations
│ │ ├── 20200925092907_Initial.Designer.cs
│ │ ├── 20200925092907_Initial.cs
│ │ └── ApiDbContextModelSnapshot.cs
│ └── Seeders
│ │ └── CustomersSeeder.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── appsettings.Development.json
└── appsettings.json
├── Dotnet2020.Api
├── ApiConfiguration.cs
├── Diagnostics
│ └── Log.cs
├── Dotnet2020.Api.csproj
└── Features
│ ├── DependencyInjection
│ ├── ConfigureMultitenancyOptions.cs
│ ├── Controller.cs
│ ├── GoodScopeHostedService.cs
│ ├── MultitenancyOptions.cs
│ └── MultitenantService.cs
│ ├── EFCore
│ ├── ApiDbContext.cs
│ ├── CompiledQueries.cs
│ ├── Controller.cs
│ └── Customer.cs
│ ├── HttpContext
│ ├── BadAuthorizationService.cs
│ ├── Controller.cs
│ └── GoogAuthorizationService.cs
│ └── Security
│ ├── Controller.cs
│ └── CustomerDto.cs
└── HighPerformanceLogging
├── HighPerformanceLogging.csproj
└── Program.cs
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-ef": {
6 | "version": "3.1.8",
7 | "commands": [
8 | "dotnet-ef"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/DotNet2020.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30413.136
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{394106D7-3EA6-4ADA-80FF-24B76D74A458}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HighPerformanceLogging", "src\HighPerformanceLogging\HighPerformanceLogging.csproj", "{8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dotnet2020.Api", "src\Dotnet2020.Api\Dotnet2020.Api.csproj", "{921A6714-08E5-4AD2-96C3-3C4116F46054}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNet2020.Host", "src\DotNet2020.Host\DotNet2020.Host.csproj", "{54AAC985-08D6-4C86-B343-8D9B42A09E58}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {921A6714-08E5-4AD2-96C3-3C4116F46054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {921A6714-08E5-4AD2-96C3-3C4116F46054}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {921A6714-08E5-4AD2-96C3-3C4116F46054}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {921A6714-08E5-4AD2-96C3-3C4116F46054}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {54AAC985-08D6-4C86-B343-8D9B42A09E58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {54AAC985-08D6-4C86-B343-8D9B42A09E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {54AAC985-08D6-4C86-B343-8D9B42A09E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {54AAC985-08D6-4C86-B343-8D9B42A09E58}.Release|Any CPU.Build.0 = Release|Any CPU
32 | EndGlobalSection
33 | GlobalSection(SolutionProperties) = preSolution
34 | HideSolutionNode = FALSE
35 | EndGlobalSection
36 | GlobalSection(NestedProjects) = preSolution
37 | {8DA3E83D-AE56-4B74-9B53-4F254C7F9BA0} = {394106D7-3EA6-4ADA-80FF-24B76D74A458}
38 | {921A6714-08E5-4AD2-96C3-3C4116F46054} = {394106D7-3EA6-4ADA-80FF-24B76D74A458}
39 | {54AAC985-08D6-4C86-B343-8D9B42A09E58} = {394106D7-3EA6-4ADA-80FF-24B76D74A458}
40 | EndGlobalSection
41 | GlobalSection(ExtensibilityGlobals) = postSolution
42 | SolutionGuid = {06469EAF-1C9B-49A3-A9AC-C63C13DB50CA}
43 | EndGlobalSection
44 | EndGlobal
45 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Asp.Net Core Best Practices 2020
4 |
5 | ## Speakers
6 |
7 | Carlos Recuero - Chapter Lead at Plain Concepts
8 |
9 | Luis Ruiz Pavón - Lead Developer at Plain Concepts ( [Github](https://github.com/lurumad) / [Twitter](https://twitter.com/luisruizpavon))
10 |
11 | ## Video
12 |
13 | Wait();
14 |
15 | ## Talk Agenda
16 |
17 | - High performance logging
18 | - Security concerns
19 | - Caching aggressively
20 | - Handling errors
21 | - Async everywhere
22 | - Compress responses
23 | - Working with files
24 | - HttpContext
25 | - Dependency Injection
26 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | # Taken from psake https://github.com/psake/psake
2 |
3 | <#
4 | .SYNOPSIS
5 | This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
6 | to see if an error occcured. If an error is detected then an exception is thrown.
7 | This function allows you to run command-line programs without having to
8 | explicitly check the $lastexitcode variable.
9 | .EXAMPLE
10 | exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
11 | #>
12 | function Exec {
13 | [CmdletBinding()]
14 | param(
15 | [Parameter(Position = 0, Mandatory = 1)][scriptblock]$cmd,
16 | [Parameter(Position = 1, Mandatory = 0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
17 | )
18 | & $cmd
19 | if ($lastexitcode -ne 0) {
20 | throw ("Exec: " + $errorMessage)
21 | }
22 | }
23 |
24 | if (Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse }
25 |
26 | exec { & dotnet restore }
27 |
28 | $suffix = "-ci-local"
29 | $commitHash = $(git rev-parse --short HEAD)
30 | $buildSuffix = "$($suffix)-$($commitHash)"
31 |
32 | Write-Output "build: Version suffix is $buildSuffix"
33 |
34 | exec { & dotnet build Balea.sln -c Release --version-suffix=$buildSuffix -v q /nologo }
35 |
36 | # echo "Running unit tests"
37 |
38 | # try {
39 |
40 | # Push-Location -Path .\tests\UnitTests
41 | # exec { & dotnet test}
42 | # } finally {
43 | # Pop-Location
44 | # }
45 |
46 | Write-Output "Starting docker containers"
47 |
48 | exec { & docker-compose -f build\docker-compose-infrastructure.yml up -d }
49 |
50 | Write-Output "Running functional tests"
51 |
52 | try {
53 |
54 | Push-Location -Path .\test\FunctionalTests
55 | exec { & dotnet test }
56 | }
57 | finally {
58 | Pop-Location
59 | }
60 |
61 | Write-Output "Finalizing docker containers"
62 | exec { & docker-compose -f build\docker-compose-infrastructure.yml down }
63 |
64 | exec { & dotnet pack .\src\Balea\Balea.csproj -c Release -o .\artifacts --include-symbols --no-build --version-suffix=$buildSuffix }
65 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #Add the dotnet path to the path
3 | export PATH="$HOME/.dotnet":$PATH
4 |
5 | if [ -d "./artifacts" ]
6 | then
7 | rm -Rf "./artifacts";
8 | fi
9 |
10 | dotnet restore
11 |
12 | commitHash=$(git rev-parse --short HEAD)
13 | suffix="-ci-local"
14 | buildSuffix="$suffix-$commitHash"
15 |
16 | echo "build: Version suffix is $buildSuffix"
17 |
18 | dotnet build -c Release --version-suffix "$buildSuffix" -v q /nologo
19 |
20 | # echo "Running unit tests"
21 | # dotnet test ./tests/UnitTests/UnitTests.csproj
22 |
23 | echo "Starting docker containers"
24 | docker-compose -f build/docker-compose-infrastructure.yml up -d
25 |
26 | echo "Runing functional tests"
27 | dotnet test ./test/FunctionalTests/FunctionalTests.csproj
28 |
29 | echo "Finalizing docker containers"
30 | docker-compose -f build/docker-compose-infrastructure.yml down
31 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "projects": [
3 | "src",
4 | "tests",
5 | "samples"
6 | ],
7 | "sdk": {
8 | "version": "3.1.301"
9 | }
10 | }
--------------------------------------------------------------------------------
/install-sdk.ps1:
--------------------------------------------------------------------------------
1 | $SdkVersion = "3.1.301"
2 | & "./dotnet-install.ps1" -Version $SdkVersion
--------------------------------------------------------------------------------
/install-sdk.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | SdkVersion="3.1.301"
4 | ./dotnet-install.sh -Version $SdkVersion
5 | export PATH="$PATH:$HOME/.dotnet"
6 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/DotNet2020.Host.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | DotNet2020Host
6 |
7 |
8 |
9 |
10 | all
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Extensions/IHostExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Logging;
4 | using System;
5 |
6 | namespace Microsoft.Extensions.Hosting
7 | {
8 | public static class IHostExtensions
9 | {
10 | public static IHost MigrateDbContext(this IHost host, Action seed = null) where TContext : DbContext
11 | {
12 | using (var scope = host.Services.CreateScope())
13 | {
14 | try
15 | {
16 | var context = scope.ServiceProvider.GetService();
17 |
18 | if (context != null)
19 | {
20 | context.Database.Migrate();
21 | seed?.Invoke(context);
22 | }
23 | }
24 | catch (Exception exception)
25 | {
26 | var logger = scope.ServiceProvider.GetService>();
27 |
28 | logger.LogError(exception, $"An error ocurred while migrating database for ${nameof(TContext)}");
29 | }
30 |
31 | return host;
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Infrastructure/Migrations/20200925092907_Initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using Dotnet2020.Api.Features.EFCore;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Metadata;
6 | using Microsoft.EntityFrameworkCore.Migrations;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | namespace DotNet2020.Host.Infrastructure.Migrations
10 | {
11 | [DbContext(typeof(ApiDbContext))]
12 | [Migration("20200925092907_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", "3.1.8")
20 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
21 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
22 |
23 | modelBuilder.Entity("Dotnet2020.Api.Features.EFCore.Customer", b =>
24 | {
25 | b.Property("Id")
26 | .ValueGeneratedOnAdd()
27 | .HasColumnType("int")
28 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
29 |
30 | b.Property("Name")
31 | .HasColumnType("nvarchar(max)");
32 |
33 | b.HasKey("Id");
34 |
35 | b.ToTable("Customers");
36 | });
37 | #pragma warning restore 612, 618
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Infrastructure/Migrations/20200925092907_Initial.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DotNet2020.Host.Infrastructure.Migrations
4 | {
5 | public partial class Initial : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.CreateTable(
10 | name: "Customers",
11 | columns: table => new
12 | {
13 | Id = table.Column(nullable: false)
14 | .Annotation("SqlServer:Identity", "1, 1"),
15 | Name = table.Column(nullable: true)
16 | },
17 | constraints: table =>
18 | {
19 | table.PrimaryKey("PK_Customers", x => x.Id);
20 | });
21 | }
22 |
23 | protected override void Down(MigrationBuilder migrationBuilder)
24 | {
25 | migrationBuilder.DropTable(
26 | name: "Customers");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Infrastructure/Migrations/ApiDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using Dotnet2020.Api.Features.EFCore;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Metadata;
6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 |
8 | namespace DotNet2020.Host.Infrastructure.Migrations
9 | {
10 | [DbContext(typeof(ApiDbContext))]
11 | partial class ApiDbContextModelSnapshot : ModelSnapshot
12 | {
13 | protected override void BuildModel(ModelBuilder modelBuilder)
14 | {
15 | #pragma warning disable 612, 618
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "3.1.8")
18 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
19 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
20 |
21 | modelBuilder.Entity("Dotnet2020.Api.Features.EFCore.Customer", b =>
22 | {
23 | b.Property("Id")
24 | .ValueGeneratedOnAdd()
25 | .HasColumnType("int")
26 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
27 |
28 | b.Property("Name")
29 | .HasColumnType("nvarchar(max)");
30 |
31 | b.HasKey("Id");
32 |
33 | b.ToTable("Customers");
34 | });
35 | #pragma warning restore 612, 618
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Infrastructure/Seeders/CustomersSeeder.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Features.EFCore;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 |
5 | namespace DotNet2020.Host.Infrastructure.Seeders
6 | {
7 | public static class CustomersSeeder
8 | {
9 | public static async Task Run(ApiDbContext dbContext)
10 | {
11 | if (!dbContext.Customers.Any())
12 | {
13 | dbContext.Customers.AddRange(new Customer[]
14 | {
15 | new Customer
16 | {
17 | Name = "John Doe"
18 | },
19 | new Customer
20 | {
21 | Name = "Mary Jean"
22 | }
23 | });
24 |
25 | await dbContext.SaveChangesAsync();
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Program.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Features.EFCore;
2 | using DotNet2020.Host.Infrastructure.Seeders;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Hosting;
5 |
6 | namespace DotNet2020.Host
7 | {
8 | public class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | CreateHostBuilder(args)
13 | .Build()
14 | .MigrateDbContext(dbContext => CustomersSeeder.Run(dbContext).Wait())
15 | .Run();
16 | }
17 |
18 | public static IHostBuilder CreateHostBuilder(string[] args) =>
19 | Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
20 | .ConfigureWebHostDefaults(webBuilder =>
21 | {
22 | webBuilder.UseStartup();
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:62580",
7 | "sslPort": 44318
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "DotNet2020.Host": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "applicationUrl": "https://localhost:5100;http://localhost:5101",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/Startup.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api;
2 | using Dotnet2020.Api.Features.EFCore;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.Extensions.Configuration;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace DotNet2020.Host
9 | {
10 | public class Startup
11 | {
12 | public IConfiguration Configuration { get; }
13 |
14 | public Startup(IConfiguration configuration)
15 | {
16 | Configuration = configuration;
17 | }
18 |
19 | public void ConfigureServices(IServiceCollection services)
20 | {
21 | ApiConfiguration.ConfigureServices(services)
22 | .AddDbContextPool(options =>
23 | {
24 | options.UseSqlServer(Configuration.GetConnectionString("SqlServer"), sqlOptions =>
25 | {
26 | sqlOptions.MigrationsAssembly(typeof(Startup).Assembly.GetName().Name);
27 | });
28 | })
29 | .AddControllers();
30 | }
31 |
32 | public void Configure(IApplicationBuilder app)
33 | {
34 | ApiConfiguration.Configure(app, _ => _, _ => _, _ => _);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/DotNet2020.Host/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "SqlServer": ""
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Information",
8 | "Microsoft": "Warning",
9 | "Microsoft.Hosting.Lifetime": "Information"
10 | }
11 | },
12 | "AllowedHosts": "*"
13 | }
14 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/ApiConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Features.DependencyInjection;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Routing;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using System;
6 |
7 | namespace Dotnet2020.Api
8 | {
9 | public static class ApiConfiguration
10 | {
11 | public static IServiceCollection ConfigureServices(IServiceCollection services)
12 | {
13 | services
14 | .AddSingleton()
15 | // Warning ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services
16 | // being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'
17 | .Configure(options =>
18 | {
19 | var serviceProvider = services.BuildServiceProvider();
20 | var multitenantService = serviceProvider.GetService();
21 | serviceProvider = services.BuildServiceProvider();
22 | multitenantService = serviceProvider.GetService();
23 | options.TenantIds = multitenantService.GetTenantIds();
24 | })
25 | //.AddSingleton, ConfigureMultitenancyOptions>()
26 | .AddHostedService()
27 | .AddHttpClient()
28 | .AddHttpContextAccessor();
29 |
30 | return services;
31 | }
32 |
33 | public static IApplicationBuilder Configure(
34 | IApplicationBuilder app,
35 | Func preRouting,
36 | Func postRouting,
37 | Func configureEndpoints)
38 | {
39 | preRouting(app);
40 | app.UseRouting();
41 | postRouting(app);
42 | app.UseEndpoints(endpoints =>
43 | {
44 | configureEndpoints(endpoints);
45 | endpoints.MapDefaultControllerRoute();
46 | });
47 |
48 | return app;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Diagnostics/Log.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using System;
3 |
4 | namespace Dotnet2020.Api.Diagnostics
5 | {
6 | static class Log
7 | {
8 | private static readonly Action startingSearchLogger =
9 | LoggerMessage.Define(
10 | LogLevel.Information,
11 | new EventId(5000, nameof(Log)),
12 | "Starting search query from {path}");
13 |
14 | private static readonly Action finishingSearchLogger =
15 | LoggerMessage.Define(
16 | LogLevel.Information,
17 | new EventId(5001, nameof(Log)),
18 | "Finishing search query from {path}.");
19 |
20 | private static readonly Action errorSearchLogger =
21 | LoggerMessage.Define(
22 | LogLevel.Error,
23 | new EventId(5002, nameof(Log)),
24 | "Failed query from {path}.");
25 |
26 | public static void StartingSearchLogger(this ILogger logger, string path)
27 | {
28 | startingSearchLogger(logger, path, null);
29 | }
30 |
31 | public static void FinishingSearchLogger(this ILogger logger, string path)
32 | {
33 | finishingSearchLogger(logger, path, null);
34 | }
35 |
36 | public static void ErrorSearchLogger(this ILogger logger, string path, Exception exception)
37 | {
38 | errorSearchLogger(logger, path, exception);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Dotnet2020.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/DependencyInjection/ConfigureMultitenancyOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Options;
3 | using System;
4 |
5 | namespace Dotnet2020.Api.Features.DependencyInjection
6 | {
7 | public class ConfigureMultitenancyOptions : IConfigureOptions
8 | {
9 | private readonly IServiceProvider serviceProvider;
10 |
11 | public ConfigureMultitenancyOptions(IServiceProvider serviceProvider)
12 | {
13 | this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
14 | }
15 |
16 | public void Configure(MultitenancyOptions options)
17 | {
18 | var multitenantService = serviceProvider.GetService();
19 | options.TenantIds = multitenantService.GetTenantIds();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/DependencyInjection/Controller.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Features.EFCore;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Options;
6 | using System.Threading.Tasks;
7 |
8 | namespace Dotnet2020.Api.Features.DependencyInjection
9 | {
10 | [Route("api/options")]
11 | [ApiController]
12 | public class Controller : ControllerBase
13 | {
14 | private readonly MultitenancyOptions options;
15 |
16 | public Controller(IOptions options)
17 | {
18 | this.options = options?.Value ?? throw new System.ArgumentNullException(nameof(options));
19 | }
20 |
21 | [HttpGet]
22 | [Route("")]
23 | public ActionResult Get()
24 | {
25 | return Ok(options.TenantIds);
26 | }
27 |
28 | [HttpGet("badfireandforget")]
29 | public IActionResult BadFireAndForget([FromServices] ApiDbContext dbContext)
30 | {
31 | _ = Task.Run(async () =>
32 | {
33 | await Task.Delay(2000);
34 |
35 | var customer = await dbContext.Customers.SingleOrDefaultAsync(c => c.Id == 1);
36 | customer.Name = "Name modified";
37 |
38 | await dbContext.SaveChangesAsync();
39 | });
40 |
41 | return Ok();
42 | }
43 |
44 | [HttpGet("goodfireandforget")]
45 | public IActionResult BadFireAndForget([FromServices] IServiceScopeFactory serviceScopeFactory)
46 | {
47 | _ = Task.Run(async () =>
48 | {
49 | await Task.Delay(2000);
50 |
51 | using var scope = serviceScopeFactory.CreateScope();
52 | var dbContext = scope.ServiceProvider.GetService();
53 | var customer = await dbContext.Customers.SingleOrDefaultAsync(c => c.Id == 1);
54 | customer.Name = "Name modified";
55 |
56 | await dbContext.SaveChangesAsync();
57 | });
58 |
59 | return Ok();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/DependencyInjection/GoodScopeHostedService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 | using System;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Dotnet2020.Api.Features.EFCore;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace Dotnet2020.Api.Features.DependencyInjection
10 | {
11 | public class GoodScopeHostedService : BackgroundService
12 | {
13 | private readonly IServiceScopeFactory serviceScopeFactory;
14 |
15 | public GoodScopeHostedService(IServiceScopeFactory serviceScopeFactory)
16 | {
17 | this.serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
18 | }
19 |
20 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
21 | {
22 | await BackgroundProcessing(stoppingToken);
23 | }
24 |
25 | private async Task BackgroundProcessing(CancellationToken cancellationToken)
26 | {
27 | while (!cancellationToken.IsCancellationRequested)
28 | {
29 | using var scope = serviceScopeFactory.CreateScope();
30 |
31 | var dbContext = scope.ServiceProvider.GetService();
32 |
33 | await dbContext.Customers.ToListAsync();
34 |
35 | await Task.Delay(5000);
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/DependencyInjection/MultitenancyOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Dotnet2020.Api.Features.DependencyInjection
5 | {
6 | public class MultitenancyOptions
7 | {
8 | public IEnumerable TenantIds { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/DependencyInjection/MultitenantService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Dotnet2020.Api.Features.DependencyInjection
5 | {
6 | public class MultitenantService
7 | {
8 | public IEnumerable GetTenantIds()
9 | {
10 | return new[] { Guid.NewGuid(), Guid.NewGuid() };
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/EFCore/ApiDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace Dotnet2020.Api.Features.EFCore
4 | {
5 | public class ApiDbContext : DbContext
6 | {
7 | public DbSet Customers { get; set; }
8 |
9 | public ApiDbContext(DbContextOptions options) : base(options)
10 | {
11 |
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/EFCore/CompiledQueries.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace Dotnet2020.Api.Features.EFCore
7 | {
8 | public static class CompiledQueries
9 | {
10 | ///
11 | /// For high-scale scenarios using EF, sometimes our queries are complex using many JOINS, Includes, filters, etc.
12 | /// EF Core can automatically compile and cache queries, but sometimes we can obtain a performance gain bypassing the computation
13 | /// of the hash of the query and the cache lookup allowing our applications to use a compiled query. This example is only to
14 | /// show how to create a compiled query but we know it's a simple query.
15 | ///
16 | public static Func> CustomerBy =
17 | EF.CompileAsyncQuery((ApiDbContext db, int id) =>
18 | db.Customers.SingleOrDefault(c => c.Id == id));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/EFCore/Controller.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.EntityFrameworkCore;
3 | using System.Collections.Generic;
4 | using System.Threading.Tasks;
5 |
6 | namespace Dotnet2020.Api.Features.EFCore
7 | {
8 | [Route("api/efcore/customers")]
9 | [ApiController]
10 | public class Controller : ControllerBase
11 | {
12 | private readonly ApiDbContext dbContext;
13 |
14 | public Controller(ApiDbContext dbContext)
15 | {
16 | this.dbContext = dbContext ?? throw new System.ArgumentNullException(nameof(dbContext));
17 | }
18 |
19 | [HttpGet]
20 | [Route("{id:int}")]
21 | public async Task> GetBy(int id)
22 | {
23 | return Ok(await CompiledQueries.CustomerBy(dbContext, id));
24 | }
25 |
26 | [HttpGet]
27 | [Route("")]
28 | public async Task>> Get()
29 | {
30 | return Ok(await dbContext.Customers.AsNoTracking().ToListAsync());
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/EFCore/Customer.cs:
--------------------------------------------------------------------------------
1 | namespace Dotnet2020.Api.Features.EFCore
2 | {
3 | public class Customer
4 | {
5 | public int Id { get; set; }
6 | public string Name { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/HttpContext/BadAuthorizationService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace Dotnet2020.Api.Features.HttpContext
4 | {
5 | public class BadAuthorizationService
6 | {
7 | public const string AdminRole = "administrator";
8 | private readonly Microsoft.AspNetCore.Http.HttpContext httpContext;
9 |
10 | public BadAuthorizationService(IHttpContextAccessor httpContextAccessor)
11 | {
12 | httpContext = httpContextAccessor.HttpContext;
13 | }
14 |
15 | public bool IsAdmin()
16 | {
17 | return httpContext.User.IsInRole(AdminRole);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/HttpContext/Controller.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Diagnostics;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Logging;
4 | using System;
5 | using System.Net.Http;
6 | using System.Threading.Tasks;
7 |
8 | namespace Dotnet2020.Api.Features.HttpContext
9 | {
10 | [Route("api/httpcontext")]
11 | [ApiController]
12 | public class HttpContextController : ControllerBase
13 | {
14 | private readonly ILogger logger;
15 | private readonly IHttpClientFactory httpClientFactory;
16 |
17 | public HttpContextController(
18 | ILogger logger,
19 | IHttpClientFactory httpClientFactory)
20 | {
21 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
22 | this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
23 | }
24 |
25 | [Route("badsearch")]
26 | public async Task BadSearch(string query)
27 | {
28 | var google = BadDoSearch($"https://www.google.com/search?q={query}");
29 | var bing = BadDoSearch($"https://www.bing.com/search?q={query}");
30 | var yandex = BadDoSearch($"https://yandex.com/search/?text={query}");
31 |
32 | await Task.WhenAll(google, bing, yandex);
33 |
34 | return Ok();
35 | }
36 |
37 | private async Task BadDoSearch(string query)
38 | {
39 | try
40 | {
41 | logger.StartingSearchLogger(HttpContext.Request.QueryString.Value);
42 |
43 | var client = httpClientFactory.CreateClient();
44 |
45 | var response = await client.GetAsync(query);
46 |
47 | logger.FinishingSearchLogger(HttpContext.Request.QueryString.Value);
48 |
49 | return await response.Content.ReadAsStringAsync();
50 | }
51 | catch (Exception exception)
52 | {
53 | logger.ErrorSearchLogger(HttpContext.Request.QueryString.Value, exception);
54 | }
55 |
56 | return string.Empty;
57 | }
58 |
59 | [Route("search")]
60 | public async Task GoodSearch(string query)
61 | {
62 | var path = HttpContext.Request.QueryString.Value;
63 | var google = GoodDoSearch($"https://www.google.com/search?q={query}", path);
64 | var bing = GoodDoSearch($"https://www.bing.com/search?q={query}", path);
65 | var yandex = GoodDoSearch($"https://yandex.com/search/?text={query}", path);
66 |
67 | await Task.WhenAll(google, bing, yandex);
68 |
69 | return Ok();
70 | }
71 |
72 | private async Task GoodDoSearch(string query, string path)
73 | {
74 | try
75 | {
76 | logger.StartingSearchLogger(path);
77 |
78 | var client = httpClientFactory.CreateClient();
79 |
80 | var response = await client.GetAsync(query);
81 |
82 | logger.FinishingSearchLogger(path);
83 |
84 | return await response.Content.ReadAsStringAsync();
85 | }
86 | catch (Exception exception)
87 | {
88 | logger.ErrorSearchLogger(path, exception);
89 | }
90 |
91 | return string.Empty;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/HttpContext/GoogAuthorizationService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace Dotnet2020.Api.Features.HttpContext
4 | {
5 | public class AuthorizationService
6 | {
7 | public const string AdminRole = "administrator";
8 | private readonly IHttpContextAccessor httpContextAccessor;
9 |
10 | public AuthorizationService(IHttpContextAccessor httpContextAccessor)
11 | {
12 | this.httpContextAccessor = httpContextAccessor ?? throw new System.ArgumentNullException(nameof(httpContextAccessor));
13 | }
14 |
15 | public bool IsAdmin()
16 | {
17 | var httpContext = httpContextAccessor.HttpContext;
18 |
19 | return httpContext is object
20 | && httpContext.User.IsInRole(AdminRole);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/Security/Controller.cs:
--------------------------------------------------------------------------------
1 | using Dotnet2020.Api.Features.EFCore;
2 | using Microsoft.AspNetCore.Mvc;
3 | using System.Threading.Tasks;
4 |
5 | namespace Dotnet2020.Api.Features.Security
6 | {
7 | [Route("api/security/customers")]
8 | [ApiController]
9 | public class Controller : ControllerBase
10 | {
11 | private readonly ApiDbContext dbContext;
12 |
13 | public Controller(ApiDbContext dbContext)
14 | {
15 | this.dbContext = dbContext ?? throw new System.ArgumentNullException(nameof(dbContext));
16 | }
17 |
18 | [HttpGet]
19 | [Route("{id:int}")]
20 | public async Task> GetBy(int id)
21 | {
22 | var customer = await CompiledQueries.CustomerBy(dbContext, id);
23 |
24 | return Ok(new CustomerDto
25 | {
26 | Id = customer.Id,
27 | Name = customer.Name
28 | });
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Dotnet2020.Api/Features/Security/CustomerDto.cs:
--------------------------------------------------------------------------------
1 | namespace Dotnet2020.Api.Features.Security
2 | {
3 | public class CustomerDto
4 | {
5 | public int Id { get; set; }
6 | public string Name { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/HighPerformanceLogging/HighPerformanceLogging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/HighPerformanceLogging/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using BenchmarkDotNet.Running;
3 | using Microsoft.Extensions.Logging;
4 | using System;
5 |
6 | namespace HighPerformanceLogging
7 | {
8 | class Program
9 | {
10 | static void Main(string[] args)
11 | {
12 | var summary = BenchmarkRunner.Run();
13 | Console.Read();
14 | }
15 | }
16 |
17 | [MemoryDiagnoser]
18 | public class LoggerMessageVsLoggerExtensionMethods
19 | {
20 | private readonly ILogger logger;
21 |
22 | public LoggerMessageVsLoggerExtensionMethods()
23 | {
24 | logger = new LoggerFactory().CreateLogger();
25 | }
26 |
27 | [Benchmark]
28 | public void ExtensionMethods()
29 | {
30 | logger.LogInformation("High performance logging {Message}", "Hello World!");
31 | }
32 |
33 | [Benchmark]
34 | public void LoggerMessage()
35 | {
36 | logger.TestLogger("Hello World!");
37 | }
38 | }
39 |
40 | static class Log
41 | {
42 | private static readonly Action _testLogger =
43 | LoggerMessage.Define(
44 | LogLevel.Information,
45 | new EventId(5000, nameof(TestLogger)),
46 | "High performance logging {Message}");
47 |
48 | public static void TestLogger(this ILogger logger, string message)
49 | {
50 | _testLogger(logger, message, null);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------