├── .dockerignore
├── .gitignore
├── Directory.Build.props
├── DotNet2019.sln
├── Nuget.config
├── README.md
├── build-agent.ps1
├── build-agent.sh
├── build.ps1
├── build
├── dependencies.props
├── docker-compose-buildagent.yml
└── docker-compose-sql.yml
├── dotnet-install.ps1
├── global.json
├── install-sdk.ps1
├── k8s
├── infra-deployment.yml
└── webapp-deployment.yml
├── src
├── DotNet2019.Api
│ ├── Configuration.cs
│ ├── Controllers
│ │ ├── PingController.cs
│ │ ├── TransientFaultController.cs
│ │ └── UsersController.cs
│ ├── DotNet2019.Api.csproj
│ ├── Extensions
│ │ ├── IEndpointRouterBuilderExtensions.cs
│ │ └── IServiceCollectionExtensions.cs
│ ├── Infrastructure
│ │ ├── Data
│ │ │ ├── DataContext.cs
│ │ │ ├── Migrations
│ │ │ │ ├── 20190616210030_initial.Designer.cs
│ │ │ │ ├── 20190616210030_initial.cs
│ │ │ │ └── DataContextModelSnapshot.cs
│ │ │ └── User.cs
│ │ ├── Hubs
│ │ │ └── DiagnosticsHub.cs
│ │ ├── Middleware
│ │ │ ├── ErrorMiddleware.cs
│ │ │ └── SecretMiddleware.cs
│ │ └── Polly
│ │ │ └── RetryPolicy.cs
│ ├── Model
│ │ └── UserRequest.cs
│ └── Services
│ │ ├── ISomeService.cs
│ │ └── SomeService.cs
└── DotNet2019.Host
│ ├── DataContent.txt
│ ├── Diagnostics
│ └── HostingDiagnosticHandler.cs
│ ├── Dockerfile
│ ├── DotNet2019.Host.csproj
│ ├── Extensions
│ ├── IApplicationBuilderExtensions.cs
│ ├── IServiceCollectionExtensions.cs
│ └── IWebHostBuilderExtensions.cs
│ ├── Infrastructure
│ └── Authentication
│ │ └── ApiKeyAuthenticationHandler.cs
│ ├── Program.cs
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ ├── index.html
│ └── lib
│ ├── signalr.min.js
│ └── signalr.min.js.map
└── test
└── FunctionalTests
├── Dockerfile
├── DotNet2019.Api
├── Api.cs
└── Controllers
│ └── UsersController.cs
├── FunctionalTests.csproj
├── Seedwork
├── Builders
│ ├── Builders.cs
│ └── UserRequestBuilder.cs
├── Extensions
│ ├── IHostBuilderExtensions.cs
│ ├── IServiceCollectionExtensions.cs
│ └── RequestBuilderExtensions.cs
├── Given.cs
├── ResetDatabaseAttribute.cs
├── ServerFixture.cs
├── ServerFixtureCollection.cs
└── TestStartup.cs
└── appsettings.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.dockerignore
2 | **/.env
3 | **/.git
4 | **/.gitignore
5 | **/.vs
6 | **/.vscode
7 | **/*.*proj.user
8 | **/azds.yaml
9 | **/charts
10 | **/bin
11 | **/obj
12 | **/Dockerfile
13 | **/Dockerfile.develop
14 | **/docker-compose.yml
15 | **/docker-compose.*.yml
16 | **/*.dbmdl
17 | **/*.jfm
18 | **/secrets.dev.yaml
19 | **/values.dev.yaml
20 | **/.toolstarget
--------------------------------------------------------------------------------
/.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 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
332 | # Test results
333 | *.trx
334 |
335 | # Container test results
336 | build/.test-results/
337 |
338 | # Code coverage files
339 | coverage.*
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | false
8 | false
9 |
10 |
11 |
--------------------------------------------------------------------------------
/DotNet2019.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29009.5
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A8DA38D7-6521-419F-A05B-61B0B53BC897}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet2019.Host", "src\DotNet2019.Host\DotNet2019.Host.csproj", "{39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet2019.Api", "src\DotNet2019.Api\DotNet2019.Api.csproj", "{4B26FFAF-E9FC-469E-93C3-AD54F226EF15}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{049FEE46-A2B7-4F7F-A5DA-C7739D0725E3}"
15 | ProjectSection(SolutionItems) = preProject
16 | build\dependencies.props = build\dependencies.props
17 | EndProjectSection
18 | EndProject
19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{5C215ACC-BDB8-464F-AB51-D334308EB3F6}"
20 | ProjectSection(SolutionItems) = preProject
21 | k8s\infra-deployment.yml = k8s\infra-deployment.yml
22 | k8s\webapp-deployment.yml = k8s\webapp-deployment.yml
23 | EndProjectSection
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\FunctionalTests\FunctionalTests.csproj", "{F3054E26-5F81-4649-86B3-99CAA95A355C}"
26 | EndProject
27 | Global
28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
29 | Debug|Any CPU = Debug|Any CPU
30 | Release|Any CPU = Release|Any CPU
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {F3054E26-5F81-4649-86B3-99CAA95A355C}.Release|Any CPU.Build.0 = Release|Any CPU
45 | EndGlobalSection
46 | GlobalSection(SolutionProperties) = preSolution
47 | HideSolutionNode = FALSE
48 | EndGlobalSection
49 | GlobalSection(NestedProjects) = preSolution
50 | {39E42FB2-78C1-43C2-9A8A-6576C7CBC6D1} = {4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}
51 | {4B26FFAF-E9FC-469E-93C3-AD54F226EF15} = {4EFF24CF-3D6A-4B08-8476-4CE3790A8D13}
52 | {F3054E26-5F81-4649-86B3-99CAA95A355C} = {A8DA38D7-6521-419F-A05B-61B0B53BC897}
53 | EndGlobalSection
54 | GlobalSection(ExtensibilityGlobals) = postSolution
55 | SolutionGuid = {65823A5F-B3A4-4143-82CC-777300A81D03}
56 | EndGlobalSection
57 | EndGlobal
58 |
--------------------------------------------------------------------------------
/Nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Asp.Net Core Good Practices 2019 Talk
4 |
5 | ## Speakers:
6 |
7 | Carlos Landeras - Web Team Lead at Plain Concepts ( [Github](https://github.com/CarlosLanderas) / [Twitter](https://twitter.com/Carlos_Lande))
8 |
9 | Luis Ruiz Pavón - DevSecOps Lead at Plain Concepts ( [Github](https://github.com/lurumad) / [Twitter](https://twitter.com/luisruizpavon))
10 |
11 | ## Talk Youtube video
12 | https://www.youtube.com/watch?v=lSIPDDxfjuw
13 |
14 | ## Talk Agenda:
15 |
16 | - Code instrumentation using System Diagnostics
17 | - Application resilience strategies
18 | - Asp.Net Core HealthChecks and Kubernetes (Readiness and Liveness Probes) for high availability and resilience
19 | - Problem Details RFC 7807
20 | - Asp.Net Core ApiController and ApiBehaviourOptions (Convention over Configuration)
21 | - HttpContext Pipelines
22 | - Endpoint Routing
23 | - Endpoint Authorization (decoupled from MVC)
24 | - Docker testing strategies
25 |
--------------------------------------------------------------------------------
/build-agent.ps1:
--------------------------------------------------------------------------------
1 | function Exec
2 | {
3 | [CmdletBinding()]
4 | param(
5 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
6 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
7 | )
8 | & $cmd
9 | if ($lastexitcode -ne 0) {
10 | throw ("Exec: " + $errorMessage)
11 | }
12 | }
13 |
14 | exec { & docker-compose -f .\build\docker-compose-buildagent.yml up --build -d }
15 | exec { & docker-compose -f .\build\docker-compose-buildagent.yml exec -e 'ConnectionStrings__SqlServer=Server=sqlserver;User Id=sa;Password=Password12!;Initial Catalog=FunctionalTests;MultipleActiveResultSets=true' functionaltests dotnet test test/FunctionalTests/FunctionalTests.csproj -p:ParallelizeTestCollections=false --logger trx --results-directory /var/temp }
16 | exec { & docker-compose -f .\build\docker-compose-buildagent.yml down }
--------------------------------------------------------------------------------
/build-agent.sh:
--------------------------------------------------------------------------------
1 | set -e
2 | export COMPOSE_INTERACTIVE_NO_CLI=1
3 | docker-compose -f './build/docker-compose-buildagent.yml' up --build -d
4 | docker-compose -f './build/docker-compose-buildagent.yml' exec -e 'ConnectionStrings__SqlServer=Server=sqlserver;User Id=sa;Password=Password12!;Initial Catalog=FunctionalTests;MultipleActiveResultSets=true' functionaltests dotnet test test/FunctionalTests/FunctionalTests.csproj -p:ParallelizeTestCollections=false --logger trx --results-directory /var/temp
5 | docker-compose -f './build/docker-compose-buildagent.yml' down
6 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | function Exec
2 | {
3 | [CmdletBinding()]
4 | param(
5 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
6 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
7 | )
8 | & $cmd
9 | if ($lastexitcode -ne 0) {
10 | throw ("Exec: " + $errorMessage)
11 | }
12 | }
13 |
14 | if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse }
15 |
16 |
17 | echo "building..."
18 |
19 | exec { & dotnet build DotNet2019.sln -c Release -v q /nologo }
20 |
21 | echo "up docker compose"
22 |
23 | #exec { & docker-compose -f .\builds\docker-compose-infrastructure.yml up -d }
24 |
25 | echo "Running functional tests"
26 |
27 | try {
28 |
29 | Push-Location -Path .\test\FunctionalTests
30 | exec { & dotnet test}
31 | } finally {
32 | Pop-Location
33 | }
34 |
35 | echo "down docker compose"
36 |
37 | #exec { & docker-compose -f .\builds\docker-compose-infrastructure.yml down }
38 |
--------------------------------------------------------------------------------
/build/dependencies.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | netcoreapp3.0
5 | preview
6 |
7 |
8 |
9 | 2.2.0
10 | 2.2.3
11 | 3.0.1
12 | 3.0.0-preview6.19304.6
13 | 5.7.0
14 | 3.2.0
15 | 2.4.1
16 | 2.4.1
17 | 16.2.0-preview-20190606-02
18 | 2.2.1
19 | 3.0.0-preview6.19307.2
20 | 3.0.0-preview6.19304.10
21 | 3.0.0-preview6.19304.10
22 | 3.0.0-preview6.19304.10
23 | 3.2.0
24 |
25 |
--------------------------------------------------------------------------------
/build/docker-compose-buildagent.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | functionaltests:
4 | image: functionaltests
5 | build:
6 | context: ..
7 | dockerfile: ./test/FunctionalTests/Dockerfile
8 | entrypoint: tail -f /dev/null
9 | depends_on:
10 | - sqlserver
11 | volumes:
12 | - .test-results/:/var/temp
13 | sqlserver:
14 | image: microsoft/mssql-server-linux
15 | restart: always
16 | environment:
17 | - ACCEPT_EULA=Y
18 | - SA_PASSWORD=Password12!
19 | ports:
20 | - 1434:1433
--------------------------------------------------------------------------------
/build/docker-compose-sql.yml:
--------------------------------------------------------------------------------
1 | version : '3.7'
2 |
3 | services:
4 | sqlserver:
5 | image: microsoft/mssql-server-linux
6 | restart: always
7 | environment:
8 | - ACCEPT_EULA=Y
9 | - SA_PASSWORD=Password12!
10 | ports:
11 | - 1434:1433
12 |
--------------------------------------------------------------------------------
/dotnet-install.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) .NET Foundation and contributors. All rights reserved.
3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 | #
5 |
6 | <#
7 | .SYNOPSIS
8 | Installs dotnet cli
9 | .DESCRIPTION
10 | Installs dotnet cli. If dotnet installation already exists in the given directory
11 | it will update it only if the requested version differs from the one already installed.
12 | .PARAMETER Channel
13 | Default: LTS
14 | Download from the Channel specified. Possible values:
15 | - Current - most current release
16 | - LTS - most current supported release
17 | - 2-part version in a format A.B - represents a specific release
18 | examples: 2.0; 1.0
19 | - Branch name
20 | examples: release/2.0.0; Master
21 | .PARAMETER Version
22 | Default: latest
23 | Represents a build version on specific channel. Possible values:
24 | - latest - most latest build on specific channel
25 | - coherent - most latest coherent build on specific channel
26 | coherent applies only to SDK downloads
27 | - 3-part version in a format A.B.C - represents specific version of build
28 | examples: 2.0.0-preview2-006120; 1.1.0
29 | .PARAMETER InstallDir
30 | Default: %LocalAppData%\Microsoft\dotnet
31 | Path to where to install dotnet. Note that binaries will be placed directly in a given directory.
32 | .PARAMETER Architecture
33 | Default: - this value represents currently running OS architecture
34 | Architecture of dotnet binaries to be installed.
35 | Possible values are: , x64 and x86
36 | .PARAMETER SharedRuntime
37 | This parameter is obsolete and may be removed in a future version of this script.
38 | The recommended alternative is '-Runtime dotnet'.
39 |
40 | Default: false
41 | Installs just the shared runtime bits, not the entire SDK.
42 | This is equivalent to specifying `-Runtime dotnet`.
43 | .PARAMETER Runtime
44 | Installs just a shared runtime, not the entire SDK.
45 | Possible values:
46 | - dotnet - the Microsoft.NETCore.App shared runtime
47 | - aspnetcore - the Microsoft.AspNetCore.App shared runtime
48 | .PARAMETER DryRun
49 | If set it will not perform installation but instead display what command line to use to consistently install
50 | currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link
51 | with specific version so that this command can be used deterministically in a build script.
52 | It also displays binaries location if you prefer to install or download it yourself.
53 | .PARAMETER NoPath
54 | By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder.
55 | If set it will display binaries location but not set any environment variable.
56 | .PARAMETER Verbose
57 | Displays diagnostics information.
58 | .PARAMETER AzureFeed
59 | Default: https://dotnetcli.azureedge.net/dotnet
60 | This parameter typically is not changed by the user.
61 | It allows changing the URL for the Azure feed used by this installer.
62 | .PARAMETER UncachedFeed
63 | This parameter typically is not changed by the user.
64 | It allows changing the URL for the Uncached feed used by this installer.
65 | .PARAMETER FeedCredential
66 | Used as a query string to append to the Azure feed.
67 | It allows changing the URL to use non-public blob storage accounts.
68 | .PARAMETER ProxyAddress
69 | If set, the installer will use the proxy when making web requests
70 | .PARAMETER ProxyUseDefaultCredentials
71 | Default: false
72 | Use default credentials, when using proxy address.
73 | .PARAMETER SkipNonVersionedFiles
74 | Default: false
75 | Skips installing non-versioned files if they already exist, such as dotnet.exe.
76 | .PARAMETER NoCdn
77 | Disable downloading from the Azure CDN, and use the uncached feed directly.
78 | #>
79 | [cmdletbinding()]
80 | param(
81 | [string]$Channel="LTS",
82 | [string]$Version="Latest",
83 | [string]$InstallDir="",
84 | [string]$Architecture="",
85 | [ValidateSet("dotnet", "aspnetcore", IgnoreCase = $false)]
86 | [string]$Runtime,
87 | [Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")]
88 | [switch]$SharedRuntime,
89 | [switch]$DryRun,
90 | [switch]$NoPath,
91 | [string]$AzureFeed="https://dotnetcli.azureedge.net/dotnet",
92 | [string]$UncachedFeed="https://dotnetcli.blob.core.windows.net/dotnet",
93 | [string]$FeedCredential,
94 | [string]$ProxyAddress,
95 | [switch]$ProxyUseDefaultCredentials,
96 | [switch]$SkipNonVersionedFiles,
97 | [switch]$NoCdn
98 | )
99 |
100 | Set-StrictMode -Version Latest
101 | $ErrorActionPreference="Stop"
102 | $ProgressPreference="SilentlyContinue"
103 |
104 | if ($NoCdn) {
105 | $AzureFeed = $UncachedFeed
106 | }
107 |
108 | $BinFolderRelativePath=""
109 |
110 | if ($SharedRuntime -and (-not $Runtime)) {
111 | $Runtime = "dotnet"
112 | }
113 |
114 | # example path with regex: shared/1.0.0-beta-12345/somepath
115 | $VersionRegEx="/\d+\.\d+[^/]+/"
116 | $OverrideNonVersionedFiles = !$SkipNonVersionedFiles
117 |
118 | function Say($str) {
119 | Write-Host "dotnet-install: $str"
120 | }
121 |
122 | function Say-Verbose($str) {
123 | Write-Verbose "dotnet-install: $str"
124 | }
125 |
126 | function Say-Invocation($Invocation) {
127 | $command = $Invocation.MyCommand;
128 | $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
129 | Say-Verbose "$command $args"
130 | }
131 |
132 | function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [int]$SecondsBetweenAttempts = 1) {
133 | $Attempts = 0
134 |
135 | while ($true) {
136 | try {
137 | return $ScriptBlock.Invoke()
138 | }
139 | catch {
140 | $Attempts++
141 | if ($Attempts -lt $MaxAttempts) {
142 | Start-Sleep $SecondsBetweenAttempts
143 | }
144 | else {
145 | throw
146 | }
147 | }
148 | }
149 | }
150 |
151 | function Get-Machine-Architecture() {
152 | Say-Invocation $MyInvocation
153 |
154 | # possible values: AMD64, IA64, x86
155 | return $ENV:PROCESSOR_ARCHITECTURE
156 | }
157 |
158 | # TODO: Architecture and CLIArchitecture should be unified
159 | function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
160 | Say-Invocation $MyInvocation
161 |
162 | switch ($Architecture.ToLower()) {
163 | { $_ -eq "" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) }
164 | { ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
165 | { $_ -eq "x86" } { return "x86" }
166 | default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" }
167 | }
168 | }
169 |
170 | function Get-Version-Info-From-Version-Text([string]$VersionText) {
171 | Say-Invocation $MyInvocation
172 |
173 | $Data = @($VersionText.Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries));
174 |
175 | $VersionInfo = @{}
176 | $VersionInfo.CommitHash = $Data[0].Trim()
177 | $VersionInfo.Version = $Data[1].Trim()
178 | return $VersionInfo
179 | }
180 |
181 | function Load-Assembly([string] $Assembly) {
182 | try {
183 | Add-Type -Assembly $Assembly | Out-Null
184 | }
185 | catch {
186 | # On Nano Server, Powershell Core Edition is used. Add-Type is unable to resolve base class assemblies because they are not GAC'd.
187 | # Loading the base class assemblies is not unnecessary as the types will automatically get resolved.
188 | }
189 | }
190 |
191 | function GetHTTPResponse([Uri] $Uri)
192 | {
193 | Invoke-With-Retry(
194 | {
195 |
196 | $HttpClient = $null
197 |
198 | try {
199 | # HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet.
200 | Load-Assembly -Assembly System.Net.Http
201 |
202 | if (-not $ProxyAddress) {
203 | try {
204 | # Despite no proxy being explicitly specified, we may still be behind a default proxy
205 | $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
206 | if ($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) {
207 | $ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString
208 | $ProxyUseDefaultCredentials = $true
209 | }
210 | } catch {
211 | # Eat the exception and move forward as the above code is an attempt
212 | # at resolving the DefaultProxy that may not have been a problem.
213 | $ProxyAddress = $null
214 | Say-Verbose("Exception ignored: $_.Exception.Message - moving forward...")
215 | }
216 | }
217 |
218 | if ($ProxyAddress) {
219 | $HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
220 | $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address=$ProxyAddress;UseDefaultCredentials=$ProxyUseDefaultCredentials}
221 | $HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
222 | }
223 | else {
224 |
225 | $HttpClient = New-Object System.Net.Http.HttpClient
226 | }
227 | # Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out
228 | # 10 minutes allows it to work over much slower connections.
229 | $HttpClient.Timeout = New-TimeSpan -Minutes 10
230 | $Response = $HttpClient.GetAsync("${Uri}${FeedCredential}").Result
231 | if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode))) {
232 | # The feed credential is potentially sensitive info. Do not log FeedCredential to console output.
233 | $ErrorMsg = "Failed to download $Uri."
234 | if ($Response -ne $null) {
235 | $ErrorMsg += " $Response"
236 | }
237 |
238 | throw $ErrorMsg
239 | }
240 |
241 | return $Response
242 | }
243 | finally {
244 | if ($HttpClient -ne $null) {
245 | $HttpClient.Dispose()
246 | }
247 | }
248 | })
249 | }
250 |
251 |
252 | function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) {
253 | Say-Invocation $MyInvocation
254 |
255 | $VersionFileUrl = $null
256 | if ($Runtime -eq "dotnet") {
257 | $VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
258 | }
259 | elseif ($Runtime -eq "aspnetcore") {
260 | $VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version"
261 | }
262 | elseif (-not $Runtime) {
263 | if ($Coherent) {
264 | $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version"
265 | }
266 | else {
267 | $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
268 | }
269 | }
270 | else {
271 | throw "Invalid value for `$Runtime"
272 | }
273 |
274 | $Response = GetHTTPResponse -Uri $VersionFileUrl
275 | $StringContent = $Response.Content.ReadAsStringAsync().Result
276 |
277 | switch ($Response.Content.Headers.ContentType) {
278 | { ($_ -eq "application/octet-stream") } { $VersionText = $StringContent }
279 | { ($_ -eq "text/plain") } { $VersionText = $StringContent }
280 | { ($_ -eq "text/plain; charset=UTF-8") } { $VersionText = $StringContent }
281 | default { throw "``$Response.Content.Headers.ContentType`` is an unknown .version file content type." }
282 | }
283 |
284 | $VersionInfo = Get-Version-Info-From-Version-Text $VersionText
285 |
286 | return $VersionInfo
287 | }
288 |
289 |
290 | function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version) {
291 | Say-Invocation $MyInvocation
292 |
293 | switch ($Version.ToLower()) {
294 | { $_ -eq "latest" } {
295 | $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False
296 | return $LatestVersionInfo.Version
297 | }
298 | { $_ -eq "coherent" } {
299 | $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True
300 | return $LatestVersionInfo.Version
301 | }
302 | default { return $Version }
303 | }
304 | }
305 |
306 | function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) {
307 | Say-Invocation $MyInvocation
308 |
309 | if ($Runtime -eq "dotnet") {
310 | $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
311 | }
312 | elseif ($Runtime -eq "aspnetcore") {
313 | $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
314 | }
315 | elseif (-not $Runtime) {
316 | $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip"
317 | }
318 | else {
319 | throw "Invalid value for `$Runtime"
320 | }
321 |
322 | Say-Verbose "Constructed primary payload URL: $PayloadURL"
323 |
324 | return $PayloadURL
325 | }
326 |
327 | function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) {
328 | Say-Invocation $MyInvocation
329 |
330 | if (-not $Runtime) {
331 | $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip"
332 | }
333 | elseif ($Runtime -eq "dotnet") {
334 | $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-win-$CLIArchitecture.$SpecificVersion.zip"
335 | }
336 | else {
337 | return $null
338 | }
339 |
340 | Say-Verbose "Constructed legacy payload URL: $PayloadURL"
341 |
342 | return $PayloadURL
343 | }
344 |
345 | function Get-User-Share-Path() {
346 | Say-Invocation $MyInvocation
347 |
348 | $InstallRoot = $env:DOTNET_INSTALL_DIR
349 | if (!$InstallRoot) {
350 | $InstallRoot = "$env:LocalAppData\Microsoft\dotnet"
351 | }
352 | return $InstallRoot
353 | }
354 |
355 | function Resolve-Installation-Path([string]$InstallDir) {
356 | Say-Invocation $MyInvocation
357 |
358 | if ($InstallDir -eq "") {
359 | return Get-User-Share-Path
360 | }
361 | return $InstallDir
362 | }
363 |
364 | function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) {
365 | Say-Invocation $MyInvocation
366 |
367 | $VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile
368 | Say-Verbose "Local version file: $VersionFile"
369 |
370 | if (Test-Path $VersionFile) {
371 | $VersionText = cat $VersionFile
372 | Say-Verbose "Local version file text: $VersionText"
373 | return Get-Version-Info-From-Version-Text $VersionText
374 | }
375 |
376 | Say-Verbose "Local version file not found."
377 |
378 | return $null
379 | }
380 |
381 | function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) {
382 | Say-Invocation $MyInvocation
383 |
384 | $DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion
385 | Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath"
386 | return Test-Path $DotnetPackagePath -PathType Container
387 | }
388 |
389 | function Get-Absolute-Path([string]$RelativeOrAbsolutePath) {
390 | # Too much spam
391 | # Say-Invocation $MyInvocation
392 |
393 | return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath)
394 | }
395 |
396 | function Get-Path-Prefix-With-Version($path) {
397 | $match = [regex]::match($path, $VersionRegEx)
398 | if ($match.Success) {
399 | return $entry.FullName.Substring(0, $match.Index + $match.Length)
400 | }
401 |
402 | return $null
403 | }
404 |
405 | function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip, [string]$OutPath) {
406 | Say-Invocation $MyInvocation
407 |
408 | $ret = @()
409 | foreach ($entry in $Zip.Entries) {
410 | $dir = Get-Path-Prefix-With-Version $entry.FullName
411 | if ($dir -ne $null) {
412 | $path = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $dir)
413 | if (-Not (Test-Path $path -PathType Container)) {
414 | $ret += $dir
415 | }
416 | }
417 | }
418 |
419 | $ret = $ret | Sort-Object | Get-Unique
420 |
421 | $values = ($ret | foreach { "$_" }) -join ";"
422 | Say-Verbose "Directories to unpack: $values"
423 |
424 | return $ret
425 | }
426 |
427 | # Example zip content and extraction algorithm:
428 | # Rule: files if extracted are always being extracted to the same relative path locally
429 | # .\
430 | # a.exe # file does not exist locally, extract
431 | # b.dll # file exists locally, override only if $OverrideFiles set
432 | # aaa\ # same rules as for files
433 | # ...
434 | # abc\1.0.0\ # directory contains version and exists locally
435 | # ... # do not extract content under versioned part
436 | # abc\asd\ # same rules as for files
437 | # ...
438 | # def\ghi\1.0.1\ # directory contains version and does not exist locally
439 | # ... # extract content
440 | function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
441 | Say-Invocation $MyInvocation
442 |
443 | Load-Assembly -Assembly System.IO.Compression.FileSystem
444 | Set-Variable -Name Zip
445 | try {
446 | $Zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath)
447 |
448 | $DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath
449 |
450 | foreach ($entry in $Zip.Entries) {
451 | $PathWithVersion = Get-Path-Prefix-With-Version $entry.FullName
452 | if (($PathWithVersion -eq $null) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) {
453 | $DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName)
454 | $DestinationDir = Split-Path -Parent $DestinationPath
455 | $OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath))
456 | if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) {
457 | New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
458 | [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles)
459 | }
460 | }
461 | }
462 | }
463 | finally {
464 | if ($Zip -ne $null) {
465 | $Zip.Dispose()
466 | }
467 | }
468 | }
469 |
470 | function DownloadFile([Uri]$Uri, [string]$OutPath) {
471 | if ($Uri -notlike "http*") {
472 | Say-Verbose "Copying file from $Uri to $OutPath"
473 | Copy-Item $Uri.AbsolutePath $OutPath
474 | return
475 | }
476 |
477 | $Stream = $null
478 |
479 | try {
480 | $Response = GetHTTPResponse -Uri $Uri
481 | $Stream = $Response.Content.ReadAsStreamAsync().Result
482 | $File = [System.IO.File]::Create($OutPath)
483 | $Stream.CopyTo($File)
484 | $File.Close()
485 | }
486 | finally {
487 | if ($Stream -ne $null) {
488 | $Stream.Dispose()
489 | }
490 | }
491 | }
492 |
493 | function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolderRelativePath) {
494 | $BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath)
495 | if (-Not $NoPath) {
496 | Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process."
497 | $env:path = "$BinPath;" + $env:path
498 | }
499 | else {
500 | Say "Binaries of dotnet can be found in $BinPath"
501 | }
502 | }
503 |
504 | $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
505 | $SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version
506 | $DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
507 | $LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
508 |
509 | if ($DryRun) {
510 | Say "Payload URLs:"
511 | Say "Primary - $DownloadLink"
512 | if ($LegacyDownloadLink) {
513 | Say "Legacy - $LegacyDownloadLink"
514 | }
515 | Say "Repeatable invocation: .\$($MyInvocation.Line)"
516 | exit 0
517 | }
518 |
519 | $InstallRoot = Resolve-Installation-Path $InstallDir
520 | Say-Verbose "InstallRoot: $InstallRoot"
521 |
522 | if ($Runtime -eq "dotnet") {
523 | $assetName = ".NET Core Runtime"
524 | $dotnetPackageRelativePath = "shared\Microsoft.NETCore.App"
525 | }
526 | elseif ($Runtime -eq "aspnetcore") {
527 | $assetName = "ASP.NET Core Runtime"
528 | $dotnetPackageRelativePath = "shared\Microsoft.AspNetCore.App"
529 | }
530 | elseif (-not $Runtime) {
531 | $assetName = ".NET Core SDK"
532 | $dotnetPackageRelativePath = "sdk"
533 | }
534 | else {
535 | throw "Invalid value for `$Runtime"
536 | }
537 |
538 | # Check if the SDK version is already installed.
539 | $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
540 | if ($isAssetInstalled) {
541 | Say "$assetName version $SpecificVersion is already installed."
542 | Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
543 | exit 0
544 | }
545 |
546 | New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
547 |
548 | $installDrive = $((Get-Item $InstallRoot).PSDrive.Name);
549 | $free = Get-CimInstance -Class win32_logicaldisk | where Deviceid -eq "${installDrive}:"
550 | if ($free.Freespace / 1MB -le 100 ) {
551 | Say "There is not enough disk space on drive ${installDrive}:"
552 | exit 0
553 | }
554 |
555 | $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
556 | Say-Verbose "Zip path: $ZipPath"
557 | Say "Downloading link: $DownloadLink"
558 | try {
559 | DownloadFile -Uri $DownloadLink -OutPath $ZipPath
560 | }
561 | catch {
562 | Say "Cannot download: $DownloadLink"
563 | if ($LegacyDownloadLink) {
564 | $DownloadLink = $LegacyDownloadLink
565 | $ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
566 | Say-Verbose "Legacy zip path: $ZipPath"
567 | Say "Downloading legacy link: $DownloadLink"
568 | DownloadFile -Uri $DownloadLink -OutPath $ZipPath
569 | }
570 | else {
571 | throw "Could not download $assetName version $SpecificVersion"
572 | }
573 | }
574 |
575 | Say "Extracting zip from $DownloadLink"
576 | Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
577 |
578 | # Check if the SDK version is now installed; if not, fail the installation.
579 | $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
580 | if (!$isAssetInstalled) {
581 | throw "$assetName version $SpecificVersion failed to install with an unknown error."
582 | }
583 |
584 | Remove-Item $ZipPath
585 |
586 | Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
587 |
588 | Say "Installation finished"
589 | exit 0
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "projects": [ "src", "tests", "samples"],
3 | "sdk": {
4 | "version": "3.0.100-preview6-012264"
5 | }
6 | }
--------------------------------------------------------------------------------
/install-sdk.ps1:
--------------------------------------------------------------------------------
1 | $SdkVersion = "3.0.100-preview6-012264"
2 | & "./dotnet-install.ps1" -Version $SdkVersion
--------------------------------------------------------------------------------
/k8s/infra-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | name: sqlserver
5 | spec:
6 |
7 | template:
8 | metadata:
9 | labels:
10 | app: sqlserver
11 | spec:
12 | containers:
13 | - name: sqlserver
14 | image: microsoft/mssql-server-linux
15 | env:
16 | - name : ACCEPT_EULA
17 | value: "Y"
18 | - name : SA_PASSWORD
19 | value: "Password12!"
20 | ---
21 |
22 | kind: Service
23 | apiVersion: v1
24 | metadata:
25 | name: sqlserver
26 | spec:
27 | selector:
28 | app: sqlserver
29 | type: ClusterIP
30 | ports:
31 | -
32 | port: 1433
33 | targetPort: 1433
34 |
35 | ---
36 |
37 | apiVersion: extensions/v1beta1
38 | kind: Deployment
39 | metadata:
40 | name: redis
41 | spec:
42 |
43 | template:
44 | metadata:
45 | labels:
46 | app: redis
47 | spec:
48 | containers:
49 | - name: redis
50 | image: redis
51 | ---
52 | kind: Service
53 | apiVersion: v1
54 | metadata:
55 | name: redis
56 | spec:
57 | selector:
58 | app: redis
59 | type: ClusterIP
60 | ports:
61 | -
62 | port: 6379
63 | targetPort: 6379
64 |
65 |
--------------------------------------------------------------------------------
/k8s/webapp-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | name: webapp-deploy
5 | spec:
6 | replicas: 3
7 | template:
8 | metadata:
9 | labels:
10 | app: webapp-deploy
11 | spec:
12 | containers:
13 | - name: webapp
14 | image: webapp
15 | imagePullPolicy: Never
16 | livenessProbe:
17 | httpGet:
18 | path: /health
19 | port: 80
20 | scheme: HTTP
21 | initialDelaySeconds: 10
22 | periodSeconds: 15
23 | readinessProbe:
24 | httpGet:
25 | path: /ready
26 | port: 80
27 | scheme: HTTP
28 | initialDelaySeconds: 10
29 | periodSeconds: 15
30 |
31 | ---
32 | kind: Service
33 | apiVersion: v1
34 | metadata:
35 | name: webapp-service
36 | spec:
37 | selector:
38 | app: webapp-deploy
39 | type: NodePort
40 | ports:
41 | - port: 80
42 | targetPort: 80
43 | nodePort: 30000
44 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Configuration.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Data;
2 | using DotNet2019.Api.Infrastructure.Hubs;
3 | using DotNet2019.Api.Infrastructure.Middleware;
4 | using Hellang.Middleware.ProblemDetails;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Http;
8 | using Microsoft.EntityFrameworkCore;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using System;
11 |
12 | namespace DotNet2019.Api
13 | {
14 | public static class Configuration
15 | {
16 | public static IServiceCollection ConfigureServices(IServiceCollection services, IWebHostEnvironment environment)
17 | {
18 | return services
19 |
20 | .AddHttpContextAccessor()
21 | .AddCustomMvc()
22 | .AddCustomMiddlewares()
23 | .AddCustomProblemDetails(environment)
24 | .AddCustomApiBehaviour()
25 | .AddCustomServices()
26 | .AddSignalR()
27 | .Services;
28 |
29 | }
30 |
31 | public static IApplicationBuilder Configure(
32 | IApplicationBuilder app,
33 | Func configureHost)
34 | {
35 | return configureHost(app)
36 | .UseProblemDetails()
37 | .UseRouting()
38 | .UseAuthentication()
39 | .UseAuthorization()
40 | .UseMiddleware()
41 | .UseEndpoints(endpoints =>
42 | {
43 | endpoints.MapControllerRoute(
44 | name: "default",
45 | pattern: "{controller=Home}/{action=Index}/{id?}");
46 | endpoints.MapRazorPages();
47 |
48 | endpoints.MapGet("/", async context =>
49 | {
50 | await context.Response.WriteAsync($"Hello DotNet 2019! from {System.Environment.MachineName}");
51 | });
52 |
53 | endpoints.MapSecretEndpoint().RequireAuthorization("ApiKeyPolicy");
54 |
55 | endpoints.MapHub("/diagnostics-hub");
56 | });
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Controllers/PingController.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Services;
2 | using Microsoft.AspNetCore.Mvc;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace DotNet2019.Api.Controllers
7 | {
8 | [ApiController]
9 | [Route("api/ping")]
10 | public class PingController : ControllerBase
11 | {
12 | private readonly ISomeService someService;
13 |
14 | public PingController(ISomeService someService)
15 | {
16 | this.someService = someService ?? throw new ArgumentNullException(nameof(someService));
17 | }
18 |
19 | [HttpGet, Route("")]
20 | public async Task Get()
21 | {
22 | return Ok(await someService.Ping());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Controllers/TransientFaultController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.AspNetCore.Mvc;
3 | using System;
4 |
5 | namespace DotNet2019.Api.Controllers
6 | {
7 | [ApiController]
8 | [Route("api/error")]
9 | public class TransientFaultController : ControllerBase
10 | {
11 | [HttpGet, Route("")]
12 | public IActionResult Get()
13 | {
14 | var value = new Random().Next(2);
15 |
16 | if (value % 2 == 0)
17 | {
18 | return Ok("Pong!");
19 | }
20 |
21 | return new ContentResult { Content = ":(", StatusCode = StatusCodes.Status500InternalServerError };
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Controllers/UsersController.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Data;
2 | using DotNet2019.Api.Model;
3 | using Microsoft.AspNetCore.Mvc;
4 | using System;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace DotNet2019.Api.Controllers
9 | {
10 | [ApiController]
11 | [Route("api/users")]
12 | public class UsersController : ControllerBase
13 | {
14 | private readonly DataContext _context;
15 |
16 | public UsersController(DataContext context)
17 | {
18 | _context = context;
19 | }
20 | [HttpPost, Route("")]
21 | public async Task Post(UserRequest request)
22 | {
23 | var isValid = ModelState.IsValid;
24 |
25 | var user = new User
26 | {
27 | Name = request.Name,
28 | Created = DateTime.UtcNow
29 | };
30 |
31 | await _context.Users.AddAsync(user);
32 | await _context.SaveChangesAsync();
33 |
34 | return Ok(user);
35 | }
36 |
37 | [HttpGet, Route("{id:int}")]
38 | public IActionResult Get(int id)
39 | {
40 | return Ok(_context.Users.FirstOrDefault(u => u.Id == id));
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/DotNet2019.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(NetCoreTargetVersion)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Extensions/IEndpointRouterBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Middleware;
2 | using Microsoft.AspNetCore.Routing;
3 |
4 | namespace Microsoft.AspNetCore.Builder
5 | {
6 | public static class IEndpointRouterBuilderExtensions
7 | {
8 | public static IEndpointConventionBuilder MapSecretEndpoint(this IEndpointRouteBuilder endpoints)
9 | {
10 | var pipeline = endpoints.CreateApplicationBuilder()
11 | .UseMiddleware()
12 | .Build();
13 |
14 | return endpoints.Map("/secret", pipeline);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Extensions/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Middleware;
2 | using DotNet2019.Api.Infrastructure.Polly;
3 | using DotNet2019.Api.Services;
4 | using Hellang.Middleware.ProblemDetails;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using System;
9 |
10 | namespace Microsoft.Extensions.DependencyInjection
11 | {
12 | public static class IServiceCollectionExtensions
13 | {
14 | public static IServiceCollection AddCustomMvc(this IServiceCollection services) =>
15 | services
16 | .AddMvc()
17 | .AddApplicationPart(typeof(IServiceCollectionExtensions).Assembly)
18 | .Services;
19 |
20 | public static IServiceCollection AddCustomMiddlewares(this IServiceCollection services) =>
21 | services
22 | .AddSingleton()
23 | .AddSingleton();
24 |
25 | public static IServiceCollection AddCustomServices(this IServiceCollection services) =>
26 | services.
27 | AddHttpClient()
28 | .SetHandlerLifetime(TimeSpan.FromMinutes(5))
29 | .AddPolicyHandler((serviceProvider, request) => RetryPolicy.GetPolicyWithJitterStrategy(serviceProvider))
30 | .Services;
31 |
32 | public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services, IWebHostEnvironment environment) =>
33 | services
34 | .AddProblemDetails(configure =>
35 | {
36 | configure.IncludeExceptionDetails = _ => environment.EnvironmentName == "Development";
37 | });
38 |
39 | public static IServiceCollection AddCustomApiBehaviour(this IServiceCollection services)
40 | {
41 |
42 | return services.Configure(options =>
43 | {
44 | options.SuppressModelStateInvalidFilter = false;
45 | options.SuppressInferBindingSourcesForParameters = false;
46 |
47 | options.InvalidModelStateResponseFactory = context =>
48 | {
49 | var problemDetails = new ValidationProblemDetails(context.ModelState)
50 | {
51 | Instance = context.HttpContext.Request.Path,
52 | Status = StatusCodes.Status400BadRequest,
53 | Type = $"https://httpstatuses.com/400",
54 | Detail = "Please refer to the errors property for additional details."
55 | };
56 | return new BadRequestObjectResult(problemDetails)
57 | {
58 | ContentTypes =
59 | {
60 | "application/problem+json",
61 | "application/problem+xml"
62 | }
63 | };
64 | };
65 | });
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Data/DataContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace DotNet2019.Api.Infrastructure.Data
7 | {
8 | public class DataContext: DbContext
9 | {
10 | internal DbSet Users { get; set; }
11 | public DataContext(DbContextOptions options) : base(options) { }
12 |
13 | protected override void OnModelCreating(ModelBuilder modelBuilder)
14 | {
15 | var user = modelBuilder.Entity();
16 |
17 | user.HasKey(u => u.Id);
18 | user.Property(u => u.Id).ValueGeneratedOnAdd();
19 | user.Property(u => u.Name).IsRequired().HasMaxLength(200);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Data/Migrations/20190616210030_initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using DotNet2019.Api.Infrastructure.Data;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 |
10 | namespace DotNet2019.Api.Infrastructure.Data.Migrations
11 | {
12 | [DbContext(typeof(DataContext))]
13 | [Migration("20190616210030_initial")]
14 | partial class initial
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasAnnotation("ProductVersion", "3.0.0-preview6.19304.10")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
23 |
24 | modelBuilder.Entity("DotNet2019.Api.Infrastructure.Data.User", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
29 |
30 | b.Property("Created");
31 |
32 | b.Property("Name")
33 | .IsRequired()
34 | .HasMaxLength(200);
35 |
36 | b.HasKey("Id");
37 |
38 | b.ToTable("Users");
39 | });
40 | #pragma warning restore 612, 618
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Data/Migrations/20190616210030_initial.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Metadata;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | namespace DotNet2019.Api.Infrastructure.Data.Migrations
6 | {
7 | public partial class initial : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.CreateTable(
12 | name: "Users",
13 | columns: table => new
14 | {
15 | Id = table.Column(nullable: false)
16 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
17 | Name = table.Column(maxLength: 200, nullable: false),
18 | Created = table.Column(nullable: false)
19 | },
20 | constraints: table =>
21 | {
22 | table.PrimaryKey("PK_Users", x => x.Id);
23 | });
24 | }
25 |
26 | protected override void Down(MigrationBuilder migrationBuilder)
27 | {
28 | migrationBuilder.DropTable(
29 | name: "Users");
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Data/Migrations/DataContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using DotNet2019.Api.Infrastructure.Data;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | namespace DotNet2019.Api.Infrastructure.Data.Migrations
10 | {
11 | [DbContext(typeof(DataContext))]
12 | partial class DataContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasAnnotation("ProductVersion", "3.0.0-preview6.19304.10")
19 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
21 |
22 | modelBuilder.Entity("DotNet2019.Api.Infrastructure.Data.User", b =>
23 | {
24 | b.Property("Id")
25 | .ValueGeneratedOnAdd()
26 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
27 |
28 | b.Property("Created");
29 |
30 | b.Property("Name")
31 | .IsRequired()
32 | .HasMaxLength(200);
33 |
34 | b.HasKey("Id");
35 |
36 | b.ToTable("Users");
37 | });
38 | #pragma warning restore 612, 618
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Data/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace DotNet2019.Api.Infrastructure.Data
6 | {
7 | internal class User
8 | {
9 | public int Id { get; set; }
10 | public string Name { get; set; }
11 | public DateTime Created { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Hubs/DiagnosticsHub.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace DotNet2019.Api.Infrastructure.Hubs
8 | {
9 | public class DiagnosticsHub: Hub
10 | {
11 | public override async Task OnConnectedAsync()
12 | {
13 | await Clients.All.SendAsync("Send", $"{Context.ConnectionId} joined");
14 | }
15 |
16 | public override async Task OnDisconnectedAsync(Exception ex)
17 | {
18 | await Clients.Others.SendAsync("Send", $"{Context.ConnectionId} left");
19 | }
20 |
21 | public Task SendRequestTrace(string message)
22 | {
23 | return Clients.All.SendAsync("SendRequestTrace", message);
24 | }
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Middleware/ErrorMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace DotNet2019.Api.Infrastructure.Middleware
6 | {
7 | public class ErrorMiddleware : IMiddleware
8 | {
9 | public Task InvokeAsync(HttpContext context, RequestDelegate next)
10 | {
11 | if (context.Request.Path.StartsWithSegments("/middleware", out _, out var remaining))
12 | {
13 | if (remaining.StartsWithSegments("/error"))
14 | {
15 | throw new Exception("This is an exception thrown from middleware.");
16 | }
17 | }
18 |
19 | return next(context);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Middleware/SecretMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DotNet2019.Api.Infrastructure.Middleware
9 | {
10 | public class SecretMiddleware : IMiddleware
11 | {
12 | private readonly DiagnosticListener _diagnosticListener;
13 | private ReadOnlyMemory fileContent;
14 | private ReadOnlyMemory startParagraph;
15 | private ReadOnlyMemory endParagraph;
16 |
17 | public SecretMiddleware(DiagnosticListener diagnosticListener)
18 | {
19 | _diagnosticListener = diagnosticListener;
20 | fileContent = File.ReadAllBytes("DataContent.txt");
21 | startParagraph = Encoding.UTF8.GetBytes("
");
22 | endParagraph = Encoding.UTF8.GetBytes("
");
23 | }
24 | public async Task InvokeAsync(HttpContext context, RequestDelegate next)
25 | {
26 | var bytesConsumed = 0;
27 | var linePosition = 0;
28 | var writer = context.Response.BodyWriter;
29 | context.Response.ContentType = "text/html";
30 |
31 | do
32 | {
33 | linePosition = fileContent.Slice(bytesConsumed).Span.IndexOf((byte)'\n');
34 |
35 | if (linePosition >= 0)
36 | {
37 | var lineLength = (bytesConsumed + linePosition) - bytesConsumed;
38 |
39 | if(lineLength > 1)
40 | {
41 | await writer.WriteAsync(startParagraph);
42 | await writer.WriteAsync(fileContent.Slice(bytesConsumed, lineLength));
43 | await writer.WriteAsync(endParagraph);
44 | }
45 |
46 | bytesConsumed += lineLength + 1;
47 | }
48 | }
49 | while (linePosition >= 0);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Infrastructure/Polly/RetryPolicy.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Services;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Logging;
4 | using Polly;
5 | using Polly.Extensions.Http;
6 | using System;
7 | using System.Net;
8 | using System.Net.Http;
9 |
10 | namespace DotNet2019.Api.Infrastructure.Polly
11 | {
12 | public static class RetryPolicy
13 | {
14 | public static IAsyncPolicy GetPolicy(IServiceProvider serviceProvider)
15 | {
16 | return HttpPolicyExtensions
17 | .HandleTransientHttpError()
18 | .OrResult(message => message.StatusCode == HttpStatusCode.InternalServerError)
19 | .WaitAndRetryAsync(
20 | retryCount: 6,
21 | sleepDurationProvider: retryAttemp => TimeSpan.FromSeconds(Math.Pow(2, retryAttemp)),
22 | onRetry: (outcome, timespan, retryAttempt, context) =>
23 | {
24 | serviceProvider.GetService>()
25 | .LogWarning("Delaying for {delay}ms, then making retry {retry}.", timespan.TotalMilliseconds, retryAttempt);
26 | });
27 | }
28 |
29 | public static IAsyncPolicy GetPolicyWithJitterStrategy(IServiceProvider serviceProvider)
30 | {
31 | var jitterer = new Random();
32 |
33 | return HttpPolicyExtensions
34 | .HandleTransientHttpError()
35 | .OrResult(message => message.StatusCode == HttpStatusCode.InternalServerError)
36 | .WaitAndRetryAsync(
37 | retryCount: 6,
38 | sleepDurationProvider: retryAttemp =>
39 | TimeSpan.FromSeconds(Math.Pow(2, retryAttemp)) +
40 | TimeSpan.FromMilliseconds(jitterer.Next(0, 100)),
41 | onRetry: (outcome, timespan, retryAttempt, context) =>
42 | {
43 | serviceProvider.GetService>()
44 | .LogWarning("Delaying for {delay}ms, then making retry {retry} with jitter strategy.", timespan.TotalMilliseconds, retryAttempt);
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Model/UserRequest.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace DotNet2019.Api.Model
4 | {
5 | ///
6 | /// Using data annotations as Fluent Validation package is not working with 3.0 preview yet.
7 | ///
8 | public class UserRequest
9 | {
10 | [Required]
11 | public string Name { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Services/ISomeService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace DotNet2019.Api.Services
4 | {
5 | public interface ISomeService
6 | {
7 | Task Ping();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/DotNet2019.Api/Services/SomeService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Threading.Tasks;
4 |
5 | namespace DotNet2019.Api.Services
6 | {
7 | public class SomeService : ISomeService
8 | {
9 | private readonly HttpClient httpClient;
10 |
11 | public SomeService(HttpClient httpClient)
12 | {
13 | this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
14 | }
15 |
16 | public Task Ping()
17 | {
18 | return httpClient.GetStringAsync("http://localhost:5001/api/error");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/DataContent.txt:
--------------------------------------------------------------------------------
1 | Enjoyed minutes related as at on on. Is fanny dried as often me. Goodness as reserved raptures to mistaken steepest oh screened he. Gravity he mr sixteen esteems. Mile home its new way with high told said. Finished no horrible blessing landlord dwelling dissuade if. Rent fond am he in on read. Anxious cordial demands settled entered in do to colonel.
2 |
3 | Silent sir say desire fat him letter. Whatever settling goodness too and honoured she building answered her. Strongly thoughts remember mr to do consider debating. Spirits musical behaved on we he farther letters. Repulsive he he as deficient newspaper dashwoods we. Discovered her his pianoforte insipidity entreaties. Began he at terms meant as fancy. Breakfast arranging he if furniture we described on. Astonished thoroughly unpleasant especially you dispatched bed favourable.
4 |
5 | At distant inhabit amongst by. Appetite welcomed interest the goodness boy not. Estimable education for disposing pronounce her. John size good gay plan sent old roof own. Inquietude saw understood his friendship frequently yet. Nature his marked ham wished.
6 |
7 | He as compliment unreserved projecting. Between had observe pretend delight for believe. Do newspaper questions consulted sweetness do. Our sportsman his unwilling fulfilled departure law. Now world own total saved above her cause table. Wicket myself her square remark the should far secure sex. Smiling cousins warrant law explain for whether.
8 |
9 | Sociable on as carriage my position weddings raillery consider. Peculiar trifling absolute and wandered vicinity property yet. The and collecting motionless difficulty son. His hearing staying ten colonel met. Sex drew six easy four dear cold deny. Moderate children at of outweigh it. Unsatiable it considered invitation he travelling insensible. Consulted admitting oh mr up as described acuteness propriety moonlight.
10 |
11 | Guest it he tears aware as. Make my no cold of need. He been past in by my hard. Warmly thrown oh he common future. Otherwise concealed favourite frankness on be at dashwoods defective at. Sympathize interested simplicity at do projecting increasing terminated. As edward settle limits at in.
12 |
13 | From they fine john he give of rich he. They age and draw mrs like. Improving end distrusts may instantly was household applauded incommode. Why kept very ever home mrs. Considered sympathize ten uncommonly occasional assistance sufficient not. Letter of on become he tended active enable to. Vicinity relation sensible sociable surprise screened no up as.
14 |
15 | He my polite be object oh change. Consider no mr am overcame yourself throwing sociable children. Hastily her totally conduct may. My solid by stuff first smile fanny. Humoured how advanced mrs elegance sir who. Home sons when them dine do want to. Estimating themselves unsatiable imprudence an he at an. Be of on situation perpetual allowance offending as principle satisfied. Improved carriage securing are desirous too.
16 |
17 | Cordially convinced did incommode existence put out suffering certainly. Besides another and saw ferrars limited ten say unknown. On at tolerably depending do perceived. Luckily eat joy see own shyness minuter. So before remark at depart. Did son unreserved themselves indulgence its. Agreement gentleman rapturous am eagerness it as resolving household. Direct wicket little of talked lasted formed or it. Sweetness consulted may prevailed for bed out sincerity.
18 |
19 | Up am intention on dependent questions oh elsewhere september. No betrayed pleasure possible jointure we in throwing. And can event rapid any shall woman green. Hope they dear who its bred. Smiling nothing affixed he carried it clothes calling he no. Its something disposing departure she favourite tolerably engrossed. Truth short folly court why she their balls. Excellence put unaffected reasonable mrs introduced conviction she. Nay particular delightful but unpleasant for uncommonly who.
20 |
21 |
--------------------------------------------------------------------------------
/src/DotNet2019.Host/Diagnostics/HostingDiagnosticHandler.cs:
--------------------------------------------------------------------------------
1 | using DotNet2019.Api.Infrastructure.Hubs;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.AspNetCore.SignalR;
4 | using Microsoft.EntityFrameworkCore.Diagnostics;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using Newtonsoft.Json;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Diagnostics;
11 | using System.Text;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace DotNet2019.Host.Diagnostics
16 | {
17 | public class HostingDiagnosticHandler : IHostedService, IObserver>, IObserver, IDisposable
18 | {
19 | private readonly DiagnosticListener _diagnosticListener;
20 | private readonly IServiceScopeFactory _scopeFactory;
21 | private readonly IHubContext _diagnosticsHub;
22 | private IDisposable _subscription;
23 | private List _listenersSubscriptions = new List();
24 | private static Dictionary traces = new Dictionary();
25 | private string requestBegin = "---------- REQUEST {0} BEGIN ----------\n";
26 | private string requestEnd = "---------- REQUEST {0} END ----------\n";
27 |
28 | public HostingDiagnosticHandler(DiagnosticListener diagnosticListener, IServiceScopeFactory scopeFactory, IHubContext diagnosticsHub)
29 | {
30 | _diagnosticListener = diagnosticListener;
31 | _scopeFactory = scopeFactory;
32 | _diagnosticsHub = diagnosticsHub;
33 | }
34 |
35 | public Task StartAsync(CancellationToken cancellationToken)
36 | {
37 | _subscription = DiagnosticListener.AllListeners.Subscribe(this);
38 | return Task.CompletedTask;
39 | }
40 |
41 | public Task StopAsync(CancellationToken cancellationToken)
42 | {
43 | return Task.CompletedTask;
44 | }
45 |
46 | public void OnNext(DiagnosticListener value)
47 | {
48 | _listenersSubscriptions.Add(value.Subscribe(this));
49 | }
50 |
51 | public void OnNext(KeyValuePair @event)
52 | {
53 | switch (@event.Key)
54 | {
55 |
56 | case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start":
57 | {
58 | var context = GetContext(@event.Value);
59 | var requestId = Guid.NewGuid();
60 | context.Items.Add("RequestId", requestId);
61 |
62 | var builder = new StringBuilder(string.Format(requestBegin, requestId))
63 | .Append($"[HttpStart] Incoming request {context.Request.Path} VERB: {context.Request.Method}\n");
64 | traces[requestId.ToString()] = builder;
65 | }
66 | break;
67 | case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop":
68 | {
69 | var context = GetContext(@event.Value);
70 | var requestId = context.Items["RequestId"].ToString();
71 |
72 | var trace = (traces[requestId.ToString()]
73 | .Append($"[HttpStop] Request finished with code {context.Response.StatusCode} \n")
74 | .Append(string.Format(requestEnd, requestId))
75 | .ToString());
76 |
77 | Task.Run(async () =>
78 | {
79 | await _diagnosticsHub.Clients.All.SendAsync("SendDiagnotics", trace);
80 | });
81 |
82 | traces.Remove(requestId);
83 | }
84 | break;
85 |
86 | case "Microsoft.AspNetCore.Diagnostics.UnhandledException":
87 | {
88 | var context = GetContext(@event.Value);
89 | var ex = @event.Value as Exception;
90 | var requestId = context.Items["RequestId"].ToString();
91 | traces[requestId].Append($"[Exception] Request {requestId} failed with error {ex.Message}\n");
92 | }
93 | break;
94 | case "Microsoft.AspNetCore.Mvc.AfterActionResult":
95 | {
96 | var actionContext = GetProperty